home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / v cisle / sadanastroju / delicious_bookmarks-2.0.64-fx.xpi / components / nsYBookmarksStoreService2.js < prev    next >
Text File  |  2008-06-19  |  137KB  |  4,010 lines

  1. /*
  2.  * Constants
  3.  */
  4.  
  5. const nsIYBookmarksStoreService = Components.interfaces.
  6.     nsIYBookmarksStoreService;
  7. const nsISupports = Components.interfaces.nsISupports;
  8.  
  9. const CLASS_ID = Components.ID("{1ed70ced-0c94-4878-b7b5-64c51251a1cc}");
  10. const TEST_CLASS_ID = Components.ID("{732f7148-80f3-403c-a33a-2f0acb54b5b0}");
  11. const CLASS_NAME = "Access Local Bookmarks Store";
  12. const TEST_CLASS_NAME = "Access Local Bookmarks Store (Test)";
  13. const CONTRACT_ID = "@mozilla.org/ybookmarks-store-service;1";
  14. const TEST_CONTRACT_ID = "@mozilla.org/ybookmarks-store-test-service;1";
  15.  
  16. var CC = Components.classes;
  17. var CI = Components.interfaces;
  18.  
  19. const SOFAR_SEARCHED = 500;
  20. const SEARCH_TIMEOUT = 50;
  21.  
  22. /*
  23.  *
  24.  *
  25.  *       ----------------------
  26.  *  --->| NC:YBookmarksTopRoot |
  27.  * |     ----------------------
  28.  * |
  29.  * |      --------------------   contains    ----------------------   contains   ----------------------
  30.  * |-----| NC:BookmarksRoot   |------------>|  Bookmark Sequence   | ---------->| bookmark resource    |  contains
  31.  * |      --------------------              | (Anonymous resource) |            | (Anonymous resource) |<-------
  32.  * |                                         ----------------------              ----------------------         |
  33.  * |                                                                                                            |
  34.  * |                                                                                                            |
  35.  * |       ----------------------  contains    ---------------------   contains     ----------------------      |
  36.  *  -----| NC:YBookmarksTagRoot |----------->| Tag Sequence         |------------->| Tag resource         |-----
  37.  *         ----------------------            | (Anonymous resource) |              | (Anonymous resource) |
  38.  *                                            ----------------------                ----------------------
  39.  *
  40.  *  All the relationships are stored in delicious.rdf
  41.  *
  42.  */
  43.  
  44. /**
  45.  * Some documentation about bookmarks search
  46.  *
  47.  * Bookmarks search is divided into 2 parts:
  48.  * 1. Searching bookmarks and,
  49.  * 2. Searching tags.
  50.  *
  51.  * In both cases when search begins, all the observers for
  52.  * "ybookmarkSearch.begin" topic is notified
  53.  * with "tags" or "bookmarks" as the data. Subject is a javascript object
  54.  * having wrappedJSObject property set.
  55.  *
  56.  * Subject object is having the following properties.
  57.  *
  58.  *  type: "bookmarks" or "tags" depending on the type of search.
  59.  *  keyword: keyword being searched for.
  60.  *
  61.  * During the search, all the observers for "ybookmarkSearch.inProgress" topic
  62.  * are notified with "tags" or "bookmarks" as the data. Subject of the
  63.  * notification is a javascript object having wrappedJSObject property set.
  64.  *
  65.  * Following are the fields for the subject object.
  66.  *
  67.  *  type: "bookmarks" or "tags" depending on the type of search.
  68.  *  keyword: keyword being searched for.
  69.  *  objectTotal: total number of objects to search, total bookmarks when
  70.  *      type is "bookmarks" and total tags to search when type is "tags".
  71.  *  total: total number of bookmarks and tags being searched. This can be used
  72.  *      for calculating any the percentage of search completed,
  73.  *      being used by ybSidebarOverlay.xml to show the progress of search.
  74.  *  sofar: This has the number of objects done searching for. Again this is
  75.  *      number of bookmarks done searching for when type is "bookmarks" and
  76.  *      number of tags done searching for when type is "tags".
  77.  *
  78.  * At the end of search all the observers for "ybookmarkSearch.end" topic is
  79.  * notified with "tags" or "bookmarks" as the data. Subject of the notification
  80.  * is a javascript object having wrappedJSObject property set.
  81.  *
  82.  * Following are the fields of the subject object.
  83.  *
  84.  *  type: "tags" or "bookmarks" depending on the type of search.
  85.  *  keyword: keyword being searched for.
  86.  *  totalMatched: total number of objects matched. Total number of bookmarks
  87.  *      matched the keyword when type is "bookmarks" and total number of tags
  88.  *      matched when type if "tags".
  89.  *
  90.  *
  91.  *
  92.  * Bundles
  93.  * =======
  94.  * Bundles contain tag arcs and order arcs
  95.  *
  96.  *
  97.  * Favorite Tags
  98.  * =============
  99.  * Favorite tags contain the favoriteTagValue, order, and a ordered sequence of 
  100.  * bookmark resources.
  101.  *
  102.  * The ordered bookmark resources are to support aribtrary/user-defined order for the bookmarks 
  103.  * within the Favorite Tag. Obvously, this may lead to some bookkeeping troubles maintaining the correct
  104.  * bookmarks within the Favorite Tag resource. As of Febuary 2007, we don't actually show this 
  105.  * functionality in the  UI, but the support is there.  On resync, we clear out the bookmark sequence 
  106.  * with the Favorite Tag, but not the Faorite Tag resource or the order, since this data is not stored 
  107.  * on the server. The sync then takes care of rebuilding the bookmarks sequence.
  108.  *
  109.  */
  110.  
  111. const kHashPropertyBagContractID = "@mozilla.org/hash-property-bag;1";
  112. const kIWritablePropertyBag = Components.interfaces.nsIWritablePropertyBag;
  113. const HashPropertyBag = new Components.Constructor(kHashPropertyBagContractID,
  114.                                                    kIWritablePropertyBag);
  115.  
  116. const kMutableArrayContractID = "@mozilla.org/array;1";
  117. const kIMutableArray = Components.interfaces.nsIMutableArray;
  118. const NSArray = new Components.Constructor(kMutableArrayContractID,
  119.                                            kIMutableArray);
  120.  
  121. const kStringContractID = "@mozilla.org/supports-string;1";
  122. const kIString = Components.interfaces.nsISupportsString;
  123. const NSString = new Components.Constructor(kStringContractID, kIString);
  124.  
  125. const kMicrosummaryContractID = "@mozilla.org/microsummary/service;1";
  126. const kIMicrosummaryService = Components.interfaces.nsIMicrosummaryService;
  127.  
  128. const kIOContractID = "@mozilla.org/network/io-service;1";
  129. const kIOIID = CI.nsIIOService;
  130. const IOSVC = CC[kIOContractID].getService(kIOIID);
  131.  
  132. const kRSS10_NAMESPACE_URI = "http://purl.org/rss/1.0/";
  133. const kRSS09_NAMESPACE_URI = "http://my.netscape.com/rdf/simple/0.9/";
  134.  
  135. // Resource Namespaces
  136. var gRdfService = CC["@mozilla.org/rdf/rdf-service;1"].
  137.                       getService(Components.interfaces.nsIRDFService);
  138. var gRdfContainerUtils = CC["@mozilla.org/rdf/container-utils;1"].
  139.                         getService(CI.nsIRDFContainerUtils);
  140.  
  141. const NS_TRANSACTION_BASE = "http://www.mozilla.org/transaction#";
  142. const NS_BOOKMARK_BASE = "http://www.mozilla.org/bookmark#";
  143. const NS_TAG_BASE = "http://www.mozilla.org/tags#";
  144. const NS_BUNDLE_BASE = "http://www.mozilla.org/bundles#";
  145. const NS_FAVORITETAG_BASE = "http://www.mozilla.org/favoritetag#";
  146. const gNC_NS     = "http://home.netscape.com/NC-rdf#";
  147. const gRDF_NS    = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
  148. const gWEB_NS    = "http://home.netscape.com/WEB-rdf#";
  149.  
  150. const TAGS = "tags";
  151. const BOOKMARKS = "bookmarks";
  152. // namespaces
  153.  
  154. var grscTopRoot = gRdfService.GetResource("NC:YBookmarksTopRoot");
  155. var grscBookmarksRoot = gRdfService.GetResource("NC:BookmarksRoot");
  156. var grscDeliciousFeedsRoot = gRdfService.GetResource("NC:DeliciousFeedsRoot");
  157.  
  158. var grscTagRoot = gRdfService.GetResource("NC:YBookmarksTagRoot");
  159. var grscFavoriteTagsRoot = gRdfService.GetResource(
  160.     "NC:YBookmarksFavoriteTagsRoot");
  161. var grscFeedRoot = gRdfService.GetResource("NC:YBookmarksFeedRoot");
  162. var grscTransactionRoot = gRdfService.GetResource(
  163.     "NC:YBookmarksTransactionRoot");
  164. var grscBundleRoot = gRdfService.GetResource("NC:YBookmarksBundleRoot");
  165.  
  166. var grscName = gRdfService.GetResource(gNC_NS + "Name");
  167. var grscUrl = gRdfService.GetResource(gNC_NS + "URL");
  168. var grscShortcut = gRdfService.GetResource(gNC_NS + "ShortcutURL");
  169. var grscPostData = gRdfService.GetResource(gNC_NS + "PostData");
  170. var grscFeedUrl = gRdfService.GetResource(gNC_NS + "FeedURL");
  171. var grscLocked = gRdfService.GetResource(gNC_NS + "Locked");
  172. var grscLivemarkExpiration = gRdfService.GetResource(gNC_NS + "Expiration");
  173. var grscIcon = gRdfService.GetResource(gNC_NS + "Icon");
  174. var grscDesc = gRdfService.GetResource(gNC_NS + "Description");
  175. var grscWebPanel = gRdfService.GetResource(gNC_NS + "WebPanel");
  176. var grscCharset = gRdfService.GetResource(gWEB_NS + "LastCharset");
  177. var grscAddDate = gRdfService.GetResource(gNC_NS + "BookmarkAddDate");
  178. var grscDate = gRdfService.GetResource(gNC_NS + "Date");
  179. var grscPage = gRdfService.GetResource(gNC_NS + "Page");
  180. var grscReferrer = gRdfService.GetResource(gNC_NS + "Referrer");
  181. var grscVisitCount = gRdfService.GetResource(gNC_NS + "VisitCount");
  182. var grscChildCount = gRdfService.GetResource(gNC_NS + "ChildCount");
  183. var grscSync = gRdfService.GetResource(gNC_NS + "Sync");
  184. var grscLastModifiedDate = gRdfService.GetResource(gWEB_NS
  185.     + "LastModifiedDate");
  186. var grscLastVisitDate = gRdfService.GetResource(gWEB_NS + "LastVisitDate");
  187. var grscLastUpdateDate = gRdfService.GetResource(gNC_NS + "LastUpdateDate");
  188.  
  189.  
  190. var grscDelFeedUrl = gRdfService.GetResource(gNC_NS + "DelFeedURL");
  191. var grscDelFeedLastUpdate = gRdfService.GetResource(gNC_NS + "DelFeedLastUpdate");
  192. var grscDelFeedType = gRdfService.GetResource(gNC_NS + "DelFeed");
  193.  
  194. var grscType = gRdfService.GetResource(gRDF_NS + "type");
  195. var grscBookmarkType = gRdfService.GetResource(gNC_NS + "Bookmark");
  196. var grscLivemarkType = gRdfService.GetResource(gNC_NS + "Livemark");
  197. var grscLiveBookmarkType = gRdfService.GetResource(gNC_NS + "LiveBookmark");
  198. var grscHash = gRdfService.GetResource(NS_BOOKMARK_BASE + "hash");
  199. var grscMetaHash = gRdfService.GetResource(NS_BOOKMARK_BASE + "metahash");
  200. var grscTag = gRdfService.GetResource(NS_BOOKMARK_BASE + "tag");
  201. var grscTagValue = gRdfService.GetResource(NS_BOOKMARK_BASE + "tagvalue");
  202. var grscFavoriteTag = gRdfService.GetResource(NS_FAVORITETAG_BASE + "favoriteTag");
  203. var grscFavoriteTagValue = gRdfService.GetResource(NS_FAVORITETAG_BASE
  204.     + "favoriteTagValue");
  205. var grscFavoriteTagOrder = gRdfService.GetResource(NS_FAVORITETAG_BASE
  206.     + "favoriteTagOrder");
  207. var grscShared = gRdfService.GetResource(NS_BOOKMARK_BASE + "shared");
  208. var grscLocalOnly = gRdfService.GetResource(NS_BOOKMARK_BASE + "localonly");
  209. var grscBookmark = gRdfService.GetResource(NS_BOOKMARK_BASE + "bookmark");
  210. var grscState = gRdfService.GetResource(NS_TRANSACTION_BASE +
  211.       "transactionState");
  212. var grscTime = gRdfService.GetResource(NS_TRANSACTION_BASE + "transactionTime");
  213. var grscBundleValue = gRdfService.GetResource(NS_BUNDLE_BASE + "BundleValue");
  214. var grscBundleOrder = gRdfService.GetResource(NS_BUNDLE_BASE + "BundleOrder");
  215.  
  216. var grscRSS09Item = gRdfService.GetResource(kRSS09_NAMESPACE_URI + "item");
  217. var grscRSS09Channel = gRdfService.GetResource(kRSS09_NAMESPACE_URI
  218.   + "channel");
  219. var grscRSS09Title = gRdfService.GetResource(kRSS09_NAMESPACE_URI + "title");
  220. var grscRSS09Link = gRdfService.GetResource(kRSS09_NAMESPACE_URI + "link");
  221.  
  222. var grscRSS10Items = gRdfService.GetResource(kRSS10_NAMESPACE_URI + "items");
  223. var grscRSS10Channel = gRdfService.GetResource(kRSS10_NAMESPACE_URI
  224.   + "channel");
  225. var grscRSS10Title = gRdfService.GetResource(kRSS10_NAMESPACE_URI + "title");
  226. var grscRSS10Link = gRdfService.GetResource(kRSS10_NAMESPACE_URI + "link");
  227.  
  228. var glitTrue = gRdfService.GetLiteral("true");
  229.  
  230. const nsTimer = "@mozilla.org/timer;1";
  231. const nsITimer = Components.interfaces.nsITimer;
  232. const nsPreferences = "@mozilla.org/preferences-service;1";
  233. const nsIPrefBranch = Components.interfaces.nsIPrefBranch;
  234. const nsIPrefBranch2 = Components.interfaces.nsIPrefBranch2;
  235. const kPrefChangeTopic = "nsPref:changed";
  236.  
  237. const PREF_PREFIX = "extensions.ybookmarks@yahoo";
  238. const PREF_FLUSH_TIMER = PREF_PREFIX + ".flush.timer";
  239.  
  240. const FAVTAG_ORDER_CHRONO = "chrono";
  241. const FAVTAG_ORDER_CHRONO_REVERSE = "chrono_reverse";
  242. const FAVTAG_ORDER_ALPHANUM = "alphanum";
  243. const FAVTAG_ORDER_ALPHANUM_REVERSE = "alphanum_reverse";
  244. const FAVTAG_ORDER_DEFAULT = FAVTAG_ORDER_CHRONO_REVERSE;
  245.  
  246. // default Flush timer is 5 minutes
  247. const DEFAULT_FLUSH_TIMER = 1;
  248.  
  249. /**********************************************************
  250.  * Load yDebug.js
  251.  **********************************************************/
  252. ((Components.classes["@mozilla.org/moz/jssubscript-loader;1"]).getService(
  253.      Components.interfaces.mozIJSSubScriptLoader)).loadSubScript(
  254.         "chrome://ybookmarks/content/yDebug.js");
  255.  
  256. /**********************************************************
  257.  * Load ybookmarksUitl.js
  258.  **********************************************************/
  259. ((Components.classes["@mozilla.org/moz/jssubscript-loader;1"]).getService(
  260.      Components.interfaces.mozIJSSubScriptLoader)).loadSubScript(
  261.         "chrome://ybookmarks/content/ybookmarksUtils.js");
  262.  
  263. var gResourceMap = {
  264.   name: grscName,
  265.   description: grscDesc,
  266.   shared: grscShared,
  267.   shortcut: grscShortcut,
  268.   postData: grscPostData,
  269.   localOnly: grscLocalOnly  
  270. };
  271.  
  272. var trace = false;
  273. var rethrow = false;
  274.  
  275. function traceIn(name) {
  276.   if (trace) {
  277.     yDebug.print("In: " + name);
  278.   }
  279. }
  280.  
  281. function traceOut(name) {
  282.   if (trace) {
  283.     yDebug.print("Out: " + name);
  284.   }
  285. }
  286.  
  287. function logError(name, e) {
  288.   yDebug.print("Error in " + name + ": " + e, YB_LOG_MESSAGE);
  289.   if (rethrow) {
  290.     throw e;
  291.   }
  292. }
  293.  
  294. function newLivemarkChild(datasource, title, url)
  295. {
  296.   var source = gRdfService.GetAnonymousResource();
  297.   datasource.Assert(source, grscType, grscLiveBookmarkType, true);
  298.   datasource.Assert(source, grscUrl, gRdfService.GetLiteral(url), true);
  299.   datasource.Assert(source, grscName, gRdfService.GetLiteral(title),
  300.     true);
  301.   return source;
  302. }
  303.  
  304. function emptyContainer(container) {
  305.       var contents = container.GetElements();
  306.       var length = 0;
  307.       while (contents.hasMoreElements()) {
  308.         length++;
  309.         contents.getNext();
  310.       }
  311.  
  312.       for (var i = length; i > 0; --i) {
  313.         container.RemoveElementAt(i, true);
  314.       }    
  315. }
  316.  
  317. function FeedListener(datasource, livemarkContainer, channel, uri) {
  318.   this._datasource = datasource;
  319.   this._livemarkContainer = livemarkContainer;
  320.   this._channel = channel;
  321.   this._uri = uri;
  322.   this._countRead = 0;
  323. }
  324.  
  325. FeedListener.prototype = {
  326.   _uri : null,
  327.   _datasource : null,
  328.   _livemarkContainer : null,
  329.   _countRead : null,
  330.   _channel : null,
  331.   _data : Array(),
  332.   _stream : null,
  333.  
  334.   QueryInterface: function (iid) {
  335.     if (!iid.equals(Components.interfaces.nsISupports) &&
  336.         !iid.equals(Components.interfaces.nsIInterfaceRequestor) &&
  337.         !iid.equals(Components.interfaces.nsIRequestObserver) &&
  338.         !iid.equals(Components.interfaces.nsIChannelEventSink) &&
  339.         !iid.equals(Components.interfaces.nsIProgressEventSink) && // see below
  340.         !iid.equals(Components.interfaces.nsIWebProgress) && // see below
  341.         !iid.equals(Components.interfaces.nsIStreamListener)) {
  342.       throw Components.results.NS_ERROR_NO_INTERFACE;
  343.     }
  344.     return this;
  345.   },
  346.  
  347.   // nsIProgressEventSink: the only reason we support
  348.   // nsIProgressEventSink is to shut up a whole slew of xpconnect
  349.   // warnings in debug builds.  (see bug #253127)
  350.   onProgress : function (aRequest, aContext, aProgress, aProgressMax) { },
  351.   onStatus : function (aRequest, aContext, aStatus, aStatusArg) { },
  352.   addProgressListener : function(listener , notifyMask ) { },
  353.   removeProgressListener : function(listener) { },
  354.  
  355.  
  356.   // nsIInterfaceRequestor
  357.   getInterface: function (iid) {
  358.     try {
  359.       return this.QueryInterface(iid);
  360.     } catch (e) {
  361.       throw Components.results.NS_NOINTERFACE;
  362.     }
  363.   },
  364.  
  365.   // nsIRequestObserver
  366.   onStartRequest : function (aRequest, aContext) {
  367.     this._stream = CC['@mozilla.org/binaryinputstream;1'].
  368.       createInstance(CI.nsIBinaryInputStream);
  369.   },
  370.  
  371.   onStopRequest : function (aRequest, aContext, aStatusCode) {
  372.     try {
  373.       if (aStatusCode != 0) {
  374.         emptyContainer(this._livemarkContainer);
  375.         this._livemarkContainer.AppendElement(
  376.           newLivemarkChild(this._datasource, "Update failed", "about:livemarks"));
  377.         yDebug.print("Livemark " + this._uri + " not updated as statusCode is " + aStatusCode);
  378.       } else {
  379.         this._datasource.beginUpdateBatch();
  380.         emptyContainer(this._livemarkContainer);
  381.  
  382.         var p = CC["@mozilla.org/xmlextras/domparser;1"].
  383.             createInstance(CI.nsIDOMParser);      
  384.         var doc = p.parseFromBuffer(this._data, this._countRead, "text/xml");    
  385.  
  386.         if (!this._trySimpleRss(doc) && !this._tryAsRDF(doc)) {
  387.             this._livemarkContainer.AppendElement(
  388.               newLivemarkChild(this._datasource, "Update failed",
  389.                 "about:livemarks"));
  390.             yDebug.print("Livemark " + this._uri + " not updated as both rdf and rss parsing failed");
  391.         } else {
  392.             yDebug.print("Livemark " + this._uri + " updated");
  393.         }
  394.         this._datasource.endUpdateBatch();
  395.         var ttl = 3600 * 1000; // By default reload after 1 hour.
  396.  
  397.         var channel = aRequest.QueryInterface(CI.nsICachingChannel);
  398.         if (channel) {
  399.             var cei = channel.cacheToken.QueryInterface(CI.nsICacheEntryInfo);
  400.             if (cei) {
  401.                 var now = new Date();
  402.                 yDebug.print("cei.expirationTime : " + cei.expirationTime + ", now : " + now.getTime());
  403.                 var expirationTime = (1000 * cei.expirationTime) - now.getTime();
  404.                 if (expirationTime > ttl) {
  405.                     ttl = expirationTime;
  406.                 }
  407.             }
  408.         }
  409.         var t = gRdfService.GetDateLiteral(new Date(now.getTime() + ttl));
  410.         var old = this._datasource.GetTarget(this._livemarkContainer.Resource,
  411.         grscLivemarkExpiration, true);
  412.         if (old) {
  413.             this._datasource.Change(this._livemarkContainer.Resource,
  414.                 grscLivemarkExpiration, old, t, true);
  415.         } else {
  416.             this._datasource.Assert(this._livemarkContainer.Resource,
  417.                 grscLivemarkExpiration, t, true);
  418.         }
  419.  
  420.         this._channel = null;
  421.       }
  422.     } catch (e) {
  423.       logError("onStopRequest(" + this._uri + ")", e);
  424.     } finally {
  425.       this._datasource.Unassert(this._livemarkContainer.Resource,
  426.         grscLocked, glitTrue, true);
  427.     }
  428.   },
  429.  
  430.   // nsIStreamObserver
  431.   onDataAvailable : function (aRequest, aContext, aInputStream,
  432.     aOffset, aCount) {
  433.     try {
  434.         this._stream.setInputStream(aInputStream);
  435.         var chunk = this._stream.readByteArray(aCount);
  436.         this._data = this._data.concat(chunk);
  437.         this._countRead += aCount;
  438.     } catch (e) {
  439.       logError("onDataAvailable", e);
  440.     }
  441.   },
  442.  
  443.   // nsIChannelEventSink
  444.   onChannelRedirect : function (aOldChannel, aNewChannel, aFlags) {
  445.     this._channel = aNewChannel;
  446.   },
  447.  
  448.   _tryAsRDF : function(doc) {
  449.     yDebug.print("_tryAsRDF");
  450.   
  451.     var serializer = Components.classes["@mozilla.org/xmlextras/xmlserializer;1"]
  452.         .createInstance(Components.interfaces.nsIDOMSerializer);
  453.     var xmlString=serializer.serializeToString(doc.documentElement);
  454.     var ds = CC["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].
  455.       createInstance(CI.nsIRDFDataSource);
  456.     var rdfXmlParser = CC["@mozilla.org/rdf/xml-parser;1"].
  457.       createInstance(CI.nsIRDFXMLParser);
  458.     var uri = CC["@mozilla.org/network/io-service;1"].
  459.       getService(CI.nsIIOService).newURI(this._uri, null, null);
  460.     rdfXmlParser.parseString(ds, uri, xmlString);
  461.     var channelResource = ds.GetSource(grscType, grscRSS10Channel, true);
  462.     if (channelResource) {
  463.       return this._processRDF10(ds, channelResource);
  464.     } else {
  465.       channelResource = ds.GetSource(grscType, grscRSS09Channel, true);
  466.       if (channelResource) {
  467.         return this._processRDF09(ds, channelResource);
  468.       }
  469.       return false;
  470.     }
  471.   },
  472.  
  473.   _processRDF10 : function(datasource, channelResource) {
  474.     var itemsNode = datasource.GetTarget(channelResource, grscRSS10Items, true);
  475.     if (itemsNode) {
  476.       this._populateChildren(datasource,
  477.         gRdfContainerUtils.MakeSeq(datasource, itemsNode).GetElements(),
  478.         grscRSS10Title, grscRSS10Link);
  479.       return true;
  480.     }
  481.     return false;
  482.   },
  483.  
  484.   _processRDF09 : function(datasource, channelResource) {
  485.     this._populateChildren(datasource,
  486.       datasource.GetSources(grscType, grscRSS09Item, true),
  487.       grscRSS09Title, grscRSS09Link);
  488.     return true;
  489.   },
  490.  
  491.   _populateChildren : function(datasource, enumerator,
  492.     titleResource, linkResource) {
  493.     while (enumerator.hasMoreElements()) {
  494.       var r = enumerator.getNext().QueryInterface(CI.nsIRDFResource);
  495.       var title = datasource.GetTarget(r, titleResource, true);
  496.       var link = datasource.GetTarget(r, linkResource, true);
  497.       if (title && link) {
  498.         this._newLivemarkBookmark(title.QueryInterface(CI.nsIRDFLiteral).Value,
  499.           link.QueryInterface(CI.nsIRDFLiteral).Value);
  500.       }
  501.     }
  502.   },
  503.  
  504.   _trySimpleRss : function(doc) {
  505.     yDebug.print("_trySimpleRss");
  506.     
  507.     if (! doc.documentElement) {
  508.       return false;
  509.     }
  510.     var lookingForChannel = false;
  511.     var node = doc.firstChild;
  512.     var isAtom = false;
  513.     while (node) {
  514.       if (node.nodeType == CI.nsIDOMNode.ELEMENT_NODE) {
  515.         var name = node.nodeName;
  516.         if (lookingForChannel) {
  517.           if (name == "channel") {
  518.             break;
  519.           }
  520.         } else {
  521.           if (name == "rss") {
  522.             node = node.firstChild;
  523.             lookingForChannel = true;
  524.             continue;
  525.           } else if (name == "feed") {
  526.             isAtom = true;
  527.             break;
  528.           }
  529.         }
  530.       }
  531.       node = node.nextSibling;
  532.     }
  533.  
  534.     if (! node) {
  535.       return false;
  536.     }
  537.     var chElement = node.QueryInterface(CI.nsIDOMElement);
  538.  
  539.     node = chElement.firstChild;
  540.     while (node) {
  541.       if (node.nodeType == CI.nsIDOMNode.ELEMENT_NODE) {
  542.         name = node.nodeName;
  543.         if (isAtom && (name == "entry")) {
  544.           this._processEntry(node);
  545.         } else if (!isAtom && (name == "item")) {
  546.           this._processItem(node);
  547.         }
  548.       }
  549.       node = node.nextSibling;
  550.     }
  551.     return true;
  552.   },
  553.  
  554.   _processEntry : function(node) {
  555.     // TODO: Implement handler for feeds. (rather than channels)
  556.     var titleString = "", dateString = "", linkString = "";
  557.  
  558.     var childNode = node.firstChild;
  559.     while (childNode) {
  560.       if (childNode.nodeType == CI.nsIDOMNode.ELEMENT_NODE) {
  561.         childNode.QueryInterface(CI.nsIDOMElement);
  562.         var childName = childNode.nodeName;
  563.         if (childName == "title") {
  564.           var titleMode = childNode.getAttribute("mode");
  565.           var titleType = childNode.getAttribute("type");
  566.           if (titleMode == "base64") {
  567.             // No one does this in <title> except standards pendats making
  568.             // test feeds, Atom 0.3 is deprecated and RFC 4287 doesn't allow it
  569.             break;
  570.           } else if ((titleType == "text")
  571.             || (titleType == "text/plain")
  572.             || ! titleType) {
  573.             titleString = this._getTextContents(childNode);
  574.           } else if ((titleType == "html")
  575.             || ((titleType == "text/html") && (titleType != "xml"))
  576.             || (titleMode == "escaped")) {
  577.               titleString = this._getTextContents(childNode);
  578.           } else if ((titleType == "xhtml")
  579.             || (titleType == "application/xhtml")
  580.             || (titleMode == "xml")) {
  581.             titleString = this._getTextContents(childNode);
  582.           } else {
  583.             titleString = this._getTextContents(childNode);
  584.           }
  585.         } else if (childName == "link" && !linkString) {
  586.           var rel = childNode.getAttribute("rel");
  587.           if (! rel || rel == "alternate") {
  588.             linkString = childNode.getAttribute("href");
  589.           }
  590.         } else if (!dateString &&
  591.           ((childName == "pubDate") || (childName == "updated"))) {
  592.             dateString = this._getTextContents(childNode);
  593.         }
  594.       }
  595.  
  596.       childNode = childNode.nextSibling;
  597.     }
  598.     if (! titleString && dateString) {
  599.       titleString = dateString;
  600.     }
  601.  
  602.     if (titleString && linkString) {
  603.       this._newLivemarkBookmark(titleString, linkString);
  604.     }
  605.  
  606.   },
  607.  
  608.   _processItem : function(node) {
  609.     var titleString = "", dateString = "", linkString = "";
  610.  
  611.     var childNode = node.firstChild;
  612.     while (childNode) {
  613.       if (childNode.nodeType == CI.nsIDOMNode.ELEMENT_NODE) {
  614.         childNode.QueryInterface(CI.nsIDOMElement);
  615.         var childName = childNode.nodeName;
  616.         if (childName == "title") {
  617.           titleString = this._getTextContents(childNode);
  618.         } else if (childName == "link" && !linkString) {
  619.           linkString = this._getTextContents(childNode);
  620.         } else if (childName == "guid" && !linkString) {
  621.           if (childNode.getAttribute("isPermaLink") != "false") {
  622.             linkString = this._getTextContents(childNode);
  623.           }
  624.         } else if (!dateString &&
  625.           ((childName == "pubDate") || (childName == "updated"))) {
  626.             dateString = this._getTextContents(childNode);
  627.         }
  628.       }
  629.  
  630.       childNode = childNode.nextSibling;
  631.     }
  632.     if (! titleString && dateString) {
  633.       titleString = dateString;
  634.     }
  635.  
  636.     if (titleString && linkString) {
  637.       this._newLivemarkBookmark(titleString, linkString);
  638.     }
  639.  
  640.   },
  641.  
  642.   _newLivemarkBookmark : function(titleString, linkString) {
  643.     this._livemarkContainer.AppendElement(
  644.       newLivemarkChild(this._datasource, titleString, linkString));
  645.   },
  646.  
  647.   _getTextContents : function(node) {
  648.     var result = "";
  649.     var doc = node.ownerDocument;
  650.     doc.QueryInterface(CI.nsIDOMDocumentTraversal);
  651.     var treeWalker = doc.createTreeWalker(node,
  652.       CI.nsIDOMNodeFilter.SHOW_TEXT | CI.nsIDOMNodeFilter.SHOW_CDATA_SECTION,
  653.       null, true);
  654.     var curNode = treeWalker.currentNode;
  655.     while (curNode) {
  656.       try {
  657.         curNode.QueryInterface(CI.nsIDOMCharacterData);
  658.         result += curNode.data;
  659.       } catch (e) {
  660.       }
  661.       curNode = treeWalker.nextNode();
  662.     }
  663.     return result;
  664.   }
  665. }
  666.  
  667. /* Class definition */
  668.  
  669. function YBookmarksStoreService(filename) {
  670.   this._init(filename);
  671.   this.wrappedJSObject = this;
  672. }
  673.  
  674. YBookmarksStoreService.prototype = {
  675.   _searchDatasource : null, // Search datasource which holds the search results
  676.  
  677.   /**
  678.    * Local delicious datasource to store data.
  679.    */
  680.  
  681.   _datasource : null,
  682.   _fileDatasource : null,
  683.   _memoryDatastore : null,
  684.   /**
  685.    * Useful for debugging.
  686.    */
  687.   _dumpChildren : function(parent, label) {
  688.     var it = this._datasource.ArcLabelsOut(parent);
  689.     while (it.hasMoreElements()) {
  690.       var c = it.getNext().QueryInterface(CI.nsIRDFResource);
  691.       var v = this._datasource.GetTarget(parent, c, true);
  692.       try {
  693.         v = "Resource = " + v.QueryInterface(CI.nsIRDFResource).Value;
  694.       } catch (e) {
  695.         try {
  696.           v = "Literal = " + v.QueryInterface(CI.nsIRDFLiteral).Value;
  697.         } catch (e) {
  698.           v = "Neithe a resource nor a literal";
  699.         }
  700.       }
  701.       yDebug.print(label + ": " + c.Value + " " + v);
  702.     }
  703.   },
  704.  
  705.   _tagRoot : null,
  706.   _getTagRoot : function() {
  707.     if (! gRdfContainerUtils.IsSeq(this._fileDatasource, grscTagRoot)) {
  708.       yDebug.print("No longer a seq: " + grscTagRoot.Value);
  709.     }
  710.     return this._tagRoot;
  711.   },
  712.   _bookmarkRoot : null,
  713.   _getBookmarkRoot : function() {
  714.     if (! gRdfContainerUtils.IsSeq(this._fileDatasource, grscBookmarksRoot)) {
  715.       yDebug.print("No longer a seq: " + grscBookmarksRoot.Value);
  716.     }
  717.     return this._bookmarkRoot;
  718.   },
  719.    _getDeliciousFeedsRoot : function() {
  720.     if (! gRdfContainerUtils.IsSeq(this._fileDatasource, grscDeliciousFeedsRoot)) {
  721.       yDebug.print("No longer a seq: " + grscDeliciousFeedsRoot.Value);
  722.     }
  723.     return this._DeliciousFeedsRoot;
  724.   },
  725.   _transactionRoot : null,
  726.   _feedsRoot : null,
  727.   _favoriteTagsRoot : null,
  728.  
  729.   _allowDeleteAllBookmarks : false,  //should we delete all bookmarks?
  730.  
  731.   _timer : null,
  732.   _flushPending : false,
  733.   _hiddenWindow : null,
  734.   _isTagSearching : false,
  735.   _isBookmarkSearching : false,
  736.   _tagSearchTimeout : null,
  737.   _bookmarkSearchTimeout : null,
  738.   _stopBookmarkSearching : false,
  739.   _stopTagSearching : false,
  740.  
  741.   _initDatasource : function(filename) {
  742.     this._datasource = CC[
  743.       "@mozilla.org/rdf/datasource;1?name=composite-datasource"].
  744.       createInstance(CI.nsIRDFCompositeDataSource);
  745.     this._fileDatasource = CC[
  746.       "@mozilla.org/rdf/datasource;1?name=xml-datasource"].
  747.       createInstance(CI.nsIRDFRemoteDataSource);
  748.     this._fileDatasource.QueryInterface(CI.nsIRDFDataSource);
  749.     this._memoryDatastore = CC[
  750.       "@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].
  751.       createInstance(CI.nsIRDFDataSource);
  752.  
  753.     try {
  754.       this._fileDatasource.Init(this._getProfileFilePath(filename));
  755.     } catch (e) {
  756.         yDebug.print("Error in initializing RDF: " + e);
  757.     }
  758.  
  759.     try {
  760.       this._fileDatasource.Refresh(true);
  761.     } catch (e) {
  762.         yDebug.print("Error in refreshing RDF: " + e);
  763.     }
  764.  
  765.     /**
  766.      * Note: The order is important. The changes that happen to the composite
  767.      * datasource are tried in the last to first order.
  768.      */
  769.     this._datasource.AddDataSource(this._memoryDatastore);
  770.     this._datasource.AddDataSource(this._fileDatasource);
  771.  
  772.   },
  773.  
  774.   _initRoots : function() {
  775.     var result = false;      
  776.     if (! gRdfContainerUtils.IsSeq(this._fileDatasource, grscBookmarksRoot)    
  777.       || ! gRdfContainerUtils.IsSeq(this._fileDatasource, grscDeliciousFeedsRoot)
  778.       || ! gRdfContainerUtils.IsSeq(this._fileDatasource, grscTagRoot)
  779.       || ! gRdfContainerUtils.IsSeq(this._fileDatasource, grscFavoriteTagsRoot)
  780.       || ! gRdfContainerUtils.IsSeq(this._fileDatasource, grscFeedRoot)
  781.       || ! gRdfContainerUtils.IsSeq(this._fileDatasource, grscBundleRoot)
  782.       || ! gRdfContainerUtils.IsSeq(this._fileDatasource, grscTransactionRoot)) {
  783.       result = true;
  784.     }
  785.     this._bookmarkRoot = gRdfContainerUtils.MakeSeq(this._fileDatasource,
  786.       grscBookmarksRoot);
  787.     this._tagRoot = gRdfContainerUtils.MakeSeq(this._fileDatasource, grscTagRoot);
  788.     this._favoriteTagsRoot = gRdfContainerUtils.MakeSeq(this._fileDatasource,
  789.       grscFavoriteTagsRoot);
  790.     this._feedsRoot = gRdfContainerUtils.MakeSeq(this._fileDatasource,
  791.       grscFeedRoot);
  792.     this._bundleRoot = gRdfContainerUtils.MakeSeq(this._fileDatasource,
  793.       grscBundleRoot);
  794.     this._transactionRoot = gRdfContainerUtils.MakeSeq(this._fileDatasource,
  795.       grscTransactionRoot);
  796.     this._DeliciousFeedsRoot = gRdfContainerUtils.MakeSeq(this._fileDatasource,
  797.       grscDeliciousFeedsRoot); 
  798.  
  799.     // add top root which contains both bookmarks root and tag root
  800.     if (! gRdfContainerUtils.IsSeq(this._fileDatasource, grscTopRoot)) {
  801.       var topRoot = gRdfContainerUtils.MakeSeq(this._fileDatasource, grscTopRoot);
  802.       topRoot.AppendElement(grscBookmarksRoot);
  803.       topRoot.AppendElement(grscTagRoot);
  804.       topRoot.AppendElement(grscFavoriteTagsRoot);
  805.       topRoot.AppendElement(grscFeedRoot);
  806.       topRoot.AppendElement(grscBundleRoot);
  807.       topRoot.AppendElement(grscTransactionRoot);
  808.       topRoot.AppendElement(grscDeliciousFeedsRoot);
  809.       result = true;
  810.     }
  811.  
  812.     return result;
  813.   },
  814.  
  815.   _updateLivemarks : function() {
  816.     yDebug.print("Livemarks being updated...", YB_LOG_MESSAGE);
  817.     var contents = this._feedsRoot.GetElements();
  818.     while (contents.hasMoreElements()) {
  819.       var bookmark = contents.getNext().QueryInterface(CI.nsIRDFResource);
  820.       this._updateLivemarkChildren(bookmark);
  821.     }
  822.     
  823.     //Take refresh interval from prefs and use it
  824.     var prefService = Components.classes["@mozilla.org/preferences-service;1"]
  825.                                      .getService(Components.interfaces.nsIPrefBranch);
  826.     var interval = prefService.getIntPref("extensions.ybookmarks@yahoo.livemarkUpdate.interval");
  827.  
  828.     if(!interval) interval = 5; //keep 5 minutes as default
  829.     
  830.     this._hiddenWindow.setTimeout(function (self) { self._updateLivemarks(); },
  831.       interval * 60 * 1000, this);
  832.   },
  833.  
  834.   _updateLivemarkChildren : function(resource) {
  835.     try {
  836.       var srcDatasource = this._fileDatasource;
  837.       var dstDatasource = this._memoryDatastore;        
  838.       if (dstDatasource.HasAssertion(resource, grscLocked, glitTrue, true)) {
  839.         return;   // Already loading.
  840.       }
  841.       var feedUrl = srcDatasource.GetTarget(resource, grscFeedUrl, true);
  842.       if (feedUrl) {
  843.         feedUrl.QueryInterface(CI.nsIRDFLiteral);
  844.         dstDatasource.Assert(resource, grscLocked, glitTrue, true);
  845.  
  846.         var expirationDate = dstDatasource.GetTarget(resource,
  847.           grscLivemarkExpiration, true);
  848.         if (expirationDate) {
  849.           expirationDate.QueryInterface(CI.nsIRDFDate);
  850.           if (expirationDate.Value > new Date()) {
  851.             yDebug.print("Livemark " + feedUrl.Value + " is up-to-date");
  852.             dstDatasource.Unassert(resource, grscLocked, glitTrue, true);
  853.             return;
  854.           }
  855.         }
  856.         var container = gRdfContainerUtils.MakeSeq(dstDatasource, resource);
  857.  
  858.         if (! expirationDate) { // Loading for first time...
  859.           emptyContainer(container);
  860.           container.AppendElement(newLivemarkChild(dstDatasource, "Loading...",
  861.             "about:livemark-loading"));
  862.         }
  863.         var channel = IOSVC.newChannel(feedUrl.Value, null, null);
  864.         var listener = new FeedListener(this._memoryDatastore, container,
  865.           channel, feedUrl.Value);
  866.         channel.notificationCallbacks = listener;
  867.         channel.asyncOpen(listener, null);
  868.       }
  869.     } catch (e) {
  870.         logError("updateLivemarkChildren", e);        
  871.     }
  872.   },
  873.  
  874.   reloadLivemark : function(aFeedUrl) {
  875.     var res = this.isLivemarked(aFeedUrl);
  876.     if (res) {
  877.         var old = this._memoryDatastore.GetTarget(res, grscLivemarkExpiration, true);
  878.         var t = gRdfService.GetDateLiteral(0);
  879.         if (old) {
  880.             this._memoryDatastore.Change(res, grscLivemarkExpiration, old, t);
  881.         } else {
  882.             this._memoryDatastore.Assert(res, grscLivemarkExpiration, t, true);
  883.         }
  884.         this._updateLivemarkChildren(res);
  885.     }
  886.   },
  887.  
  888.   _initSearchDatasource : function() {
  889.     this._searchDatasource = CC[
  890.       "@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].
  891.       createInstance(CI.nsIRDFDataSource);
  892.   },
  893.  
  894.   _initHiddenWindow : function() {
  895.     this._hiddenWindow = CC["@mozilla.org/appshell/appShellService;1"].
  896.       getService(CI.nsIAppShellService).hiddenDOMWindow;
  897.   },
  898.  
  899.   _init : function(filename) {
  900.     yDebug.print("Initialing bookmarks service");
  901.     this._initDatasource(filename);
  902.  
  903.     if (this._initRoots()) {
  904.       this._doFlush();
  905.     }
  906.  
  907.     this._initSearchDatasource();
  908.  
  909.     this._allowDeleteAllBookmarks = true;
  910.  
  911.     // Clear completed transactions and reset other transactions'
  912.     // state to uninitialized
  913.     this._resetTransactions();
  914.  
  915.     // init the flush timer and observe the change of the flush timer pref
  916.     this._resetFlushTimer();
  917.     this._initPrefObserver();
  918.     this._initHiddenWindow();
  919.  
  920.     this._updateLivemarks();
  921.   },
  922.  
  923.   /**
  924.    * Flushing the bookmarks for all changes is not a good idea as it depends
  925.    * on number of bookmarks stored. If number of bookmarks is a large number,
  926.    * flush take long time to finish. To overcome
  927.    * this problem set the timer to every 5/10 seconds.
  928.    */
  929.   _resetFlushTimer : function() {
  930.  
  931.     if (! this._timer) {
  932.       this._timer = CC[nsTimer].createInstance(nsITimer);
  933.     } else {
  934.       this._timer.cancel();
  935.     }
  936.  
  937.     var prefs = CC[nsPreferences].getService(nsIPrefBranch);
  938.     var flushDelay = DEFAULT_FLUSH_TIMER;
  939.     try {
  940.       flushDelay = prefs.getIntPref(PREF_FLUSH_TIMER);
  941.     } catch (e) {
  942.     }
  943.     this._timer.initWithCallback(this, flushDelay * 60 * 1000,
  944.       CI.nsITimer.TYPE_REPEATING_SLACK);
  945.     yDebug.print("Timer initiated", YB_LOG_MESSAGE);
  946.   },
  947.  
  948.   _initPrefObserver : function() {
  949.     var prefs = CC[nsPreferences].getService(nsIPrefBranch2);
  950.     prefs.addObserver(PREF_PREFIX, this, false);
  951.   },
  952.  
  953.   observe : function (aSubject, aTopic, aData) {
  954.     if ((aTopic == kPrefChangeTopic) && (aData == PREF_FLUSH_TIMER)) {
  955.       this._resetFlushTimer();
  956.     }
  957.   },
  958.  
  959.   notify : function(aTimer) {
  960.     if (this._flushPending) {
  961.       this._doFlush();
  962.     }
  963.   },
  964.  
  965.   /** Immediately flush the RDF. */
  966.   _doFlush : function() {
  967.     if (this._fileDatasource) {
  968.       this._fileDatasource.Flush();
  969.     }
  970.     this._flushPending = false;
  971.   },
  972.  
  973.   _scheduleFlush : function() {
  974.     this._flushPending = true;
  975.   },
  976.  
  977.   /**
  978.    * Either immediately flush the RDF, or schedule a flush with the timer.
  979.    * If \c aForce is \c true, the flush is immediate.
  980.    */
  981.  
  982.   flush : function(aForce) {
  983.     traceIn("flush");
  984.     try {
  985.       if (aForce) {
  986.         this._doFlush();
  987.       } else {
  988.         this._scheduleFlush();
  989.       }
  990.     } catch (e) {
  991.       logError("flush", e);
  992.     } finally {
  993.       traceOut("flush");
  994.     }
  995.   },
  996.  
  997.   getDataSource : function() {
  998.     traceIn("getDataSource");
  999.     try {
  1000.       return this._datasource;
  1001.     } catch (e) {
  1002.       logError("getDataSource", e);
  1003.     } finally {
  1004.       traceOut("getDataSource");
  1005.     }
  1006.   },
  1007.  
  1008.   /**
  1009.    * Get the path of file in profile directory
  1010.    *
  1011.    * @param aName the name of the file
  1012.    * @return the file path
  1013.    */
  1014.   _getProfileFilePath : function(aName) {
  1015.     var dirService = CC["@mozilla.org/file/directory_service;1"].
  1016.                      getService(CI.nsIProperties);
  1017.     var file = dirService.get("ProfD", CI.nsILocalFile);
  1018.     file.append(aName);
  1019.     if (! file.exists()) {
  1020.       file.create(CI.nsIFile.NORMAL_FILE_TYPE, 0600);
  1021.     }
  1022.  
  1023.     var networkProtocol = CC["@mozilla.org/network/protocol;1?name=file"].
  1024.           createInstance(CI.nsIFileProtocolHandler);
  1025.     var fileURI = networkProtocol.newFileURI(file);
  1026.  
  1027.     return fileURI.spec;
  1028.   },
  1029.  
  1030.   _fromNSArrayToJSArray : function(nsarray) {
  1031.     var tags = nsarray.QueryInterface(CI.nsIArray)
  1032.       .enumerate();
  1033.     paramTags = new Array();
  1034.     while (tags.hasMoreElements()) {
  1035.       paramTags.push(tags.getNext().QueryInterface(CI.nsISupportsString).data);
  1036.     }
  1037.  
  1038.     return paramTags;
  1039.   },
  1040.  
  1041.   _addBookmark : function(aUrl, aTitle, aCharset, aIsWebPanel, aDescription,
  1042.     aShortcut, aPostData, aCountTags, aTags, aShared, localOnly, shouldFlush) {
  1043.     var bookmarkResource = this._getBookmarkResource(aUrl, false);
  1044.     if (!bookmarkResource) {
  1045.         bookmarkResource = this._getBookmarkResource(aUrl, true);
  1046.         this._setStringProperty(bookmarkResource, grscName, aTitle);
  1047.         this._setStringProperty(bookmarkResource, grscShortcut, aShortcut);
  1048.         this._setStringProperty(bookmarkResource, grscDesc, aDescription);
  1049.         this._setStringProperty(bookmarkResource, grscCharset, aCharset);
  1050.         this._setStringProperty(bookmarkResource, grscPostData, aPostData);
  1051.         this._addTags(bookmarkResource, aTags);
  1052.     } else {
  1053.       // treat this as the edit operation except for the tags.
  1054.       this.editBookmark ( aUrl,  { 
  1055.                                   name: aTitle,
  1056.                                   description: aDescription,
  1057.                                   shortcut: aShortcut,
  1058.                                   postData: aPostData,
  1059.                                   tags: aTags
  1060.                                 }
  1061.                         );
  1062.     }    
  1063.     this._setSharedFlag(bookmarkResource, aShared);
  1064.     this._setLocalOnlyFlag(bookmarkResource, localOnly);
  1065.  
  1066.     // this is for the backward compatibility during development process
  1067.     var addDate = "" + ((new Date()).getTime() * 1000);
  1068.     this._setAddDate(bookmarkResource, addDate);
  1069.  
  1070.     if (this._fileDatasource.GetTarget(bookmarkResource,
  1071.       grscLastModifiedDate, true) == null) {
  1072.       this._fileDatasource.Assert (bookmarkResource,
  1073.         grscLastModifiedDate, gRdfService.GetDateLiteral(addDate), true);
  1074.     }
  1075.  
  1076.     if (this._fileDatasource.GetTarget(bookmarkResource,
  1077.       grscVisitCount, true) == null) {
  1078.       this._fileDatasource.Assert(bookmarkResource, grscVisitCount,
  1079.         gRdfService.GetIntLiteral(0), true);
  1080.     }
  1081.  
  1082.     return bookmarkResource;
  1083.   },
  1084.  
  1085.   /**
  1086.    * From nsYBookmarkService.js - modified
  1087.    *
  1088.    */
  1089.   _addTags : function(bookmarkResource, aTags) {
  1090.     for (var count = 0; count < aTags.length; ++count) {
  1091.       if (aTags[count].length != 0) {
  1092.         var tagLiteral = gRdfService.GetLiteral(aTags[count]);
  1093.         if (! this._fileDatasource.HasAssertion(bookmarkResource,
  1094.           grscTag, tagLiteral, true)) {
  1095.           this._fileDatasource.Assert(bookmarkResource, grscTag,
  1096.             tagLiteral, true);
  1097.         }
  1098.         this._addBookmarkToTagContainer(aTags[count], bookmarkResource);
  1099.       }
  1100.     }
  1101.  
  1102.     this._addBookmarkToFavoriteTagsContainers(aTags, bookmarkResource);
  1103.   },
  1104.  
  1105.   /**
  1106.    * Change the tag to lower case
  1107.    */
  1108.   _normalizeTag : function (aTag) {
  1109.     if (aTag == "") {
  1110.       return aTag;
  1111.     }
  1112.  
  1113.     return aTag.toLowerCase();
  1114.   },
  1115.  
  1116.   /**
  1117.    * Add bookmark resource to tag container
  1118.    */
  1119.   _addBookmarkToTagContainer : function (aTag, aBookmarkResource) {
  1120.     var tagResource = this._getTagResource(aTag, true);
  1121.     var tagContainer = gRdfContainerUtils.MakeSeq(this._fileDatasource,
  1122.       tagResource);
  1123.  
  1124.     if (tagContainer.IndexOf(aBookmarkResource) == -1) {
  1125.       tagContainer.AppendElement(aBookmarkResource);
  1126.       this._changeTagTotalChildrenCounter(tagResource, true);
  1127.     }
  1128.   },
  1129.  
  1130.   /**
  1131.    * Remove bookmark resource from the tag container
  1132.    *
  1133.    * @param aTag a tag
  1134.    * @param aBookmarkResource a bookmark resource
  1135.    */
  1136.   _removeBookmarkFromTagContainer : function(aTag, aBookmarkResource) {
  1137.     var tagResource = this._getTagResource(aTag, false);
  1138.     if (tagResource) {
  1139.       var tagContainer = gRdfContainerUtils.MakeSeq(this._fileDatasource,
  1140.         tagResource);
  1141.       var index = tagContainer.IndexOf(aBookmarkResource);
  1142.       if (index != -1) {
  1143.         tagContainer.RemoveElementAt(index, false);
  1144.         this._changeTagTotalChildrenCounter(tagResource, false);
  1145.       }
  1146.     }
  1147.   },
  1148.  
  1149.   /**
  1150.    * Count the number of children in a tag container and store the total
  1151.    */
  1152.   _changeTagTotalChildrenCounter : function(tagResource, isIncrement) {
  1153.     var childCounter = this._fileDatasource.GetTarget(
  1154.       tagResource, grscChildCount, true);
  1155.     if (childCounter) {
  1156.       childCounter.QueryInterface(CI.nsIRDFInt);
  1157.       var newValue = childCounter.Value;
  1158.       if (isIncrement) {
  1159.         newValue++;
  1160.       } else {
  1161.         newValue--;
  1162.       }
  1163.  
  1164.       if (newValue == 0) {
  1165.         this._deleteAllChildren(tagResource);
  1166.         this._removeFromTagRoot(tagResource);
  1167.       } else {
  1168.         var newCounter = gRdfService.GetIntLiteral(newValue);
  1169.         this._fileDatasource.Change(tagResource, grscChildCount,
  1170.           childCounter, newCounter, true);
  1171.       }
  1172.     } else {
  1173.       // find total number of children in this first.
  1174.       var newCounter = gRdfService.GetIntLiteral(1);
  1175.       this._fileDatasource.Assert(tagResource, grscChildCount,
  1176.         newCounter, true);
  1177.     }
  1178.   },
  1179.  
  1180.   /*********************************************************************
  1181.    *                          Favorite Tag Functions                   *
  1182.    *********************************************************************/
  1183.  
  1184.   _tokenizeFavoriteTag : function(aTag) {
  1185.       return this._normalizeTag(aTag).split("+");
  1186.   },
  1187.  
  1188.   isFavoriteTag : function(aTag) {
  1189.     traceIn("isFavoriteTag");
  1190.     try {
  1191.       return this._getFavoriteTagResouce(aTag, false);
  1192.     } catch (e) {
  1193.       logError("isFavoriteTag", e);
  1194.     } finally {
  1195.       traceOut("isFavoriteTag");
  1196.     }
  1197.   },
  1198.  
  1199.   getFavoriteTags : function(aCount) {
  1200.     traceIn("getFavoriteTags");
  1201.     try {
  1202.       var out = [];
  1203.       var favTags = this._favoriteTagsRoot.GetElements();
  1204.  
  1205.       while (favTags.hasMoreElements()) {
  1206.         var ft = favTags.getNext();
  1207.         var ftNode = this._fileDatasource.GetTarget(ft, grscFavoriteTagValue, true);
  1208.         ftNode.QueryInterface(CI.nsIRDFLiteral);
  1209.         out.push(ftNode.Value);
  1210.       }
  1211.       aCount.value = out.length;
  1212.       return out;
  1213.     } catch (e) {
  1214.       logError("getFavoriteTags", e);
  1215.     } finally {
  1216.       traceOut("getFavoriteTags");
  1217.     }
  1218.   },
  1219.  
  1220.   getFavoriteTagOrder : function(aTag) {
  1221.     traceIn("getFavoriteTagOrder");
  1222.     try {
  1223.       var favTagResource = this._getFavoriteTagResouce(aTag);
  1224.       if (favTagResource) {
  1225.         var favTagOrderResource = this._fileDatasource.GetTarget(favTagResource,
  1226.           grscFavoriteTagOrder, true);
  1227.         if (favTagOrderResource) {
  1228.           favTagOrderResource.QueryInterface(CI.nsIRDFLiteral);
  1229.           return favTagOrderResource.Value;
  1230.         }
  1231.       }
  1232.       return FAVTAG_ORDER_DEFAULT;
  1233.     } catch (e) {
  1234.       logError("getFavoriteTagOrder", e);
  1235.     } finally {
  1236.       traceOut("getFavoriteTagOrder");
  1237.     }
  1238.   },
  1239.  
  1240.   setFavoriteTagOrder : function(aTag, aOrder) {
  1241.     traceIn("setFavoriteTagOrder");
  1242.     try {
  1243.       var favTagResource = this._getFavoriteTagResouce(aTag);
  1244.       if (favTagResource) {
  1245.         this._setStringProperty(favTagResource, grscFavoriteTagOrder, aOrder);
  1246.         this._scheduleFlush();
  1247.       }
  1248.     } catch (e) {
  1249.       logError("setFavoriteTagOrder", e);
  1250.     } finally {
  1251.       traceOut("setFavoriteTagOrder");
  1252.     }
  1253.   },
  1254.  
  1255.   clearFavoriteTags : function() {
  1256.     traceIn("clearFavoriteTags");
  1257.     try {
  1258.       this._deleteAllContentsAndChildren(this._favoriteTagsRoot);
  1259.       this._scheduleFlush();
  1260.     } catch (e) {
  1261.       logError("clearFavoriteTags", e);
  1262.     } finally {
  1263.       traceOut("clearFavoriteTags");
  1264.     }
  1265.   },
  1266.   
  1267.   cleanOutFavoriteTags : function() {
  1268.     traceIn("cleanOutFavoriteTags");
  1269.     try {
  1270.       var favTags = this._favoriteTagsRoot.GetElements();
  1271.       while (favTags.hasMoreElements()) {
  1272.         var favTagRsrc = favTags.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
  1273.         var bmSeq = gRdfContainerUtils.MakeSeq(this._fileDatasource, favTagRsrc);
  1274.         var bmEnum = bmSeq.GetElements();
  1275.         var elts = []; // why? cuz it's bad to change the underlying resource while iterating
  1276.         while (bmEnum.hasMoreElements()) {
  1277.           elts.push(bmEnum.getNext());
  1278.         }
  1279.         for (var i=0; i < elts.length; i++) {
  1280.           bmSeq.RemoveElement(elts[i], true);
  1281.         }
  1282.       }
  1283.       this._scheduleFlush();
  1284.     } catch (e) {
  1285.       logError("cleanOutFavoriteTags", e);
  1286.     } finally {
  1287.       traceOut("cleanOutFavoriteTags");
  1288.     }
  1289.   },
  1290.  
  1291.   addFavoriteTag : function(aTag) {
  1292.     traceIn("addFavoriteTag");
  1293.     try {
  1294.       var favTagResource = this._getFavoriteTagResouce(aTag, true);
  1295.       this._initFavoriteTag(favTagResource, aTag);
  1296.       this._scheduleFlush();
  1297.     } catch (e) {
  1298.       logError("addFavoriteTag", e);
  1299.     } finally {
  1300.       traceOut("addFavoriteTag");
  1301.     }
  1302.   },
  1303.  
  1304.   /*
  1305.    * Populates a Favorite tag with all the appropriate tags
  1306.    */
  1307.   _initFavoriteTag : function(favTagResource, aTag) {
  1308.     var tags = this._tokenizeFavoriteTag(aTag);
  1309.     var args = [];
  1310.     for (var i = 0; i < tags.length; i++) {
  1311.       var tagResource = this._getTagResource(tags[i], false);
  1312.       if (tagResource) {
  1313.         var bookmarksEnum = gRdfContainerUtils.MakeSeq(this._fileDatasource,
  1314.           tagResource).GetElements();
  1315.         args.push(bookmarksEnum);
  1316.       } else {
  1317.         args = null;
  1318.         break;
  1319.       }
  1320.     }
  1321.  
  1322.     if (args) {
  1323.       var bookmarks = this._getIntersectingBookmarks(args);
  1324.       var favTagContainer = gRdfContainerUtils.MakeSeq(this._fileDatasource,
  1325.         favTagResource);
  1326.  
  1327.       for (var i = 0; i < bookmarks.length; i++) {
  1328.         var bm = bookmarks[i];
  1329.         if (favTagContainer.IndexOf(bm) == -1) {
  1330.           favTagContainer.AppendElement(bm);
  1331.         }
  1332.       }
  1333.     }
  1334.   },
  1335.  
  1336.   /**
  1337.    * Return the intersection of bookmarks
  1338.    * aArrays - an Array of nsISimpleEnumeration that contain bookmark Resources
  1339.    */
  1340.   _getIntersectingBookmarks : function(aArrays) {
  1341.     var bookmarks = {};
  1342.     var result = [];
  1343.  
  1344.     for (var i = 0; i < aArrays.length; i++) {
  1345.       var bmEnum = aArrays[i];
  1346.       while (bmEnum.hasMoreElements()) {
  1347.         var bm = bmEnum.getNext();
  1348.         bm.QueryInterface(CI.nsIRDFResource);
  1349.         var key = bm.Value;
  1350.         if (! bookmarks[key]) {
  1351.           bookmarks[key] = { count : 1, bm : bm };
  1352.         } else {
  1353.           bookmarks[key].count++;
  1354.         }
  1355.       }
  1356.     }
  1357.  
  1358.     for each (var bm in bookmarks) {
  1359.       if (bm.count == aArrays.length) {
  1360.         result.push(bm.bm);
  1361.       }
  1362.     }
  1363.     return result;
  1364.   },
  1365.  
  1366.   deleteFavoriteTag : function (aTag) {
  1367.     traceIn("deleteFavoriteTag");
  1368.     try {
  1369.       var favTagResource = this._getFavoriteTagResouce(aTag, false);
  1370.  
  1371.       if (favTagResource) {
  1372.         this._removeMatchingContent(this._favoriteTagsRoot.Resource,
  1373.           favTagResource);
  1374.         this._deleteAllChildren(favTagResource);
  1375.         this._scheduleFlush();
  1376.       }
  1377.     } catch (e) {
  1378.       logError("deleteFavoriteTag", e);
  1379.     } finally {
  1380.       traceOut("deleteFavoriteTag");
  1381.     }
  1382.   },
  1383.  
  1384.   moveFavoriteTag : function (aTag, aIndex) {
  1385.     traceIn("moveFavoriteTag");
  1386.     try {
  1387.       var favTagResource = this._getFavoriteTagResouce(aTag);
  1388.  
  1389.       if (favTagResource &&
  1390.         (this._favoriteTagsRoot.IndexOf(favTagResource) != -1)) {
  1391.         this._favoriteTagsRoot.RemoveElement(favTagResource, true);
  1392.         this._favoriteTagsRoot.InsertElementAt(favTagResource, aIndex, true);
  1393.         this._scheduleFlush();
  1394.       }
  1395.     } catch (e) {
  1396.       logError("moveFavoriteTag", e);
  1397.     } finally {
  1398.       traceOut("moveFavoriteTag");
  1399.     }
  1400.   },
  1401.  
  1402.   getBookmarksFromFavoriteTag : function(aTag, aCount) {
  1403.     traceIn("getBookmarksFromFavoriteTag");
  1404.     try {
  1405.       var favTagResource = this._getFavoriteTagResouce(aTag);
  1406.       var out = [];
  1407.  
  1408.       if (favTagResource) {
  1409.         var tagContainer = gRdfContainerUtils.MakeSeq(this._fileDatasource,
  1410.           favTagResource);
  1411.         var favTags = tagContainer.GetElements();
  1412.         while (favTags.hasMoreElements()) {
  1413.           var ft = favTags.getNext();
  1414.           var bookmark = this.getBookmarkFromResource(ft);
  1415.           if (bookmark && bookmark.url) {
  1416.             out.push(bookmark);
  1417.           } else {
  1418.             yDebug.print("Favorite Tag '" + aTag + "' has a strange resource: " + ft.Value, YB_LOG_MESSAGE);
  1419.           }
  1420.         }
  1421.       }
  1422.  
  1423.       if (aCount) {
  1424.         aCount.value = out.length;
  1425.       }
  1426.  
  1427.       return out;
  1428.     } catch (e) {
  1429.       logError("getBookmarksFromFavoriteTag", e);
  1430.     } finally {
  1431.       traceOut("getBookmarksFromFavoriteTag");
  1432.     }
  1433.     return [];
  1434.   },
  1435.  
  1436.   _addBookmarkToFavoriteTagsContainers : function (aTags, aBookmarkResource) {
  1437.     try {
  1438.       var lookup = {};
  1439.  
  1440.       for (var i = 0; i < aTags.length; i++) {
  1441.         var tag = this._normalizeTag(aTags[i]);
  1442.         if (tag) {
  1443.           lookup[tag] = true;
  1444.         }
  1445.       }
  1446.  
  1447.       var favTags = this._favoriteTagsRoot.GetElements();
  1448.       while (favTags.hasMoreElements()) {
  1449.         var favTagResource = favTags.getNext();
  1450.         favTagResource.QueryInterface(CI.nsIRDFResource);
  1451.         var favTagValueNode = this._fileDatasource.GetTarget(favTagResource,
  1452.           grscFavoriteTagValue, true);
  1453.         favTagValueNode.QueryInterface(CI.nsIRDFLiteral);
  1454.         var favTagValue = favTagValueNode.Value;
  1455.  
  1456.         var fts = this._tokenizeFavoriteTag(favTagValue);
  1457.         var addToFavTag = true;
  1458.  
  1459.         if (fts.length > 0) {
  1460.           for (var i = 0; i < fts.length; i++) {
  1461.             if (!lookup[fts[i]]) {
  1462.               addToFavTag = false;
  1463.               break;
  1464.             }
  1465.           }
  1466.           if (addToFavTag) {
  1467.             var favTagContainer = gRdfContainerUtils.MakeSeq(
  1468.               this._fileDatasource, favTagResource);
  1469.             if (favTagContainer.IndexOf(aBookmarkResource) == -1) {
  1470.               favTagContainer.InsertElementAt(aBookmarkResource, 1, true);
  1471.             }
  1472.           }
  1473.         }
  1474.       }
  1475.     } catch (e) {
  1476.         yDebug.print("_addBookmarkToFavoriteTagsContainers(): " + e);
  1477.     }
  1478.  
  1479.   },
  1480.  
  1481.   /*********************************************************************
  1482.    *                        Bookmark Functions                         *
  1483.    *********************************************************************/
  1484.  
  1485.   /**
  1486.    * Add bookmark to the _fileDatasource. Call Flush if shouldFlush parameter
  1487.    * is set to true.
  1488.    * If bookmark already exists, do nothing.
  1489.    *
  1490.    * @param aUrl url of the bookmark. url cannot be changed once added
  1491.    * @param aTitle title of the bookmark
  1492.    * @param aCharset charset for the url
  1493.    * @param aIsWebPanel unknown. Just copied from the existing bookmark
  1494.    * @param aDescription short description for this bookmark
  1495.    * @param aShortcut shortcut for the bookmark
  1496.    * @param aPostData post data for a post form shortcut
  1497.    * @param aCountTags number of items in the next array parameter
  1498.    * @param aTags tags for this bookmark. Array of string
  1499.    * @param aShared whether or not this bookmark is shared with public
  1500.    * @param localOnly whether or not this bookmark should be stored in
  1501.    * local only
  1502.    * @param shouldFlush flag to indicate whether or not to call Flush
  1503.    * after adding bookmark.
  1504.    *
  1505.    */
  1506.   addBookmark : function(aUrl, aTitle, aCharset, aIsWebPanel, aDescription,
  1507.     aShortcut, aPostData, aCountTags, aTags, aShared, localOnly, shouldFlush) {
  1508.     traceIn("addBookmark");
  1509.     try {
  1510.       var bookmarkResource = this._addBookmark(aUrl, aTitle, aCharset, aIsWebPanel, aDescription,
  1511.         aShortcut, aPostData, aCountTags, aTags, aShared, localOnly);
  1512.       this._setProperty(bookmarkResource, grscType, grscBookmarkType);
  1513.       if (shouldFlush) {
  1514.         this._scheduleFlush();
  1515.       }
  1516.     } catch (e) {
  1517.       logError("addBookmark", e);
  1518.     } finally {
  1519.       traceOut("addBookmark");
  1520.     }
  1521.   },
  1522.  
  1523.   /**
  1524.    * Add new bookmark to rdf:bookmark _fileDatasource
  1525.    */
  1526.   addBookmarkObject : function (aBookmarkObject, shouldFlush) {
  1527.     traceIn("addBookmarkObject");
  1528.     try {
  1529.       var jsarray = this._fromNSArrayToJSArray(aBookmarkObject.tags);
  1530.       var bookmarkResource = this._addBookmark(aBookmarkObject.url,
  1531.         aBookmarkObject.name, "", false, aBookmarkObject.description,
  1532.         aBookmarkObject.shortcut, aBookmarkObject.postData,
  1533.         jsarray.length, jsarray, aBookmarkObject.shared,
  1534.         aBookmarkObject.localOnly);
  1535.       this._setProperty(bookmarkResource, grscType, grscBookmarkType);
  1536.  
  1537.       this._setDateProperty(bookmarkResource, grscLastModifiedDate,
  1538.         aBookmarkObject.last_modified);
  1539.       this._setDateProperty(bookmarkResource, grscAddDate,
  1540.         aBookmarkObject.added_date);
  1541.   
  1542.       if (shouldFlush) {
  1543.         this._scheduleFlush();
  1544.       }
  1545.     } catch (e) {
  1546.       logError("addBookmarkObject", e);
  1547.     } finally {
  1548.       traceOut("addBookmarkObject");
  1549.     }
  1550.   },
  1551.  
  1552.   /**
  1553.    * Add new livemarks to bookmarks _datasource
  1554.    *
  1555.    * @param aUrl the url of where the livemark comes from
  1556.    * @parma aTitle the title of the livemark
  1557.    * @param aFeedUrl the feed url
  1558.    * @param aDescription the description of the livemark
  1559.    * @param aCountTags size of tags array
  1560.    * @param aTags array of tags
  1561.    * @param aShared whether or not this bookmark is shared with public
  1562.    * @param localOnly whether or not this bookmark should be stored in
  1563.    * local only
  1564.    * @param shouldFlush should flush the livemarks _datasource or not
  1565.    */
  1566.   addLivemark : function(aUrl, aTitle, aFeedUrl, aDescription, aCountTags,
  1567.     aTags, aShared, localOnly, shouldFlush) {
  1568.     traceIn("addLivemark");
  1569.     try {
  1570.       var bookmarkResource = this.isLivemarked(aFeedUrl);
  1571.       if (!bookmarkResource) {
  1572.         bookmarkResource = this._getBookmarkResource(aUrl, true);
  1573.         this._feedsRoot.AppendElement(bookmarkResource);
  1574.       }
  1575.  
  1576.       this._setStringProperty(bookmarkResource, grscUrl, aUrl);
  1577.       this._setStringProperty(bookmarkResource, grscName, aTitle);
  1578.       this._setStringProperty(bookmarkResource, grscFeedUrl, aFeedUrl);
  1579.       this._setStringProperty(bookmarkResource, grscDesc, aDescription);
  1580.       this._addTags(bookmarkResource, aTags);
  1581.       this._setSharedFlag(bookmarkResource, aShared);
  1582.       this._setLocalOnlyFlag(bookmarkResource, localOnly);
  1583.       this._setProperty(bookmarkResource, grscType, grscLivemarkType);
  1584.  
  1585.       if (shouldFlush) {
  1586.         this._scheduleFlush();
  1587.       }
  1588.       this._updateLivemarkChildren(bookmarkResource);
  1589.     } catch (e) {
  1590.       logError("addLivemark", e);
  1591.     } finally {
  1592.       traceOut("addLivemark");
  1593.     }
  1594.   },
  1595.  
  1596.   /**
  1597.    * Edit the given bookmark. New values are provided as a nsIYBookmark object.
  1598.    * URL should not be allowed to be edited. Implementation is free to
  1599.    * decide on the editable attributes.
  1600.    *
  1601.    * @param aUrl url to be edited.
  1602.    * @param args object of nsIYBookmark. This is key value pair represented
  1603.    * as a JSON object.
  1604.    *
  1605.    * @return false if aUrl is not present in the database, true otherwise.
  1606.    *
  1607.    * NOTE: Method may throw an exception.
  1608.    *
  1609.    */
  1610.   editBookmark : function(aUrl, args) {
  1611.     traceIn("editBookmark");
  1612.     try {
  1613.       var bookmarkResource = this.isLivemarked(aUrl);
  1614.  
  1615.       if (!bookmarkResource) {
  1616.         bookmarkResource = this._getBookmarkResource(aUrl, false);
  1617.         if (!bookmarkResource) {
  1618.           yDebug.print("editBookmark: Bookmark not found. Returning...",
  1619.             B_LOG_MESSAGE);
  1620.           return false;
  1621.         }
  1622.       }
  1623.  
  1624.       var isChanged = false;
  1625.       for (var part in gResourceMap) {
  1626.         if (args[part]) {
  1627.           var currentValue = this._fileDatasource.GetTarget(bookmarkResource,
  1628.             gResourceMap[part], true);
  1629.           if (currentValue) {
  1630.             this._fileDatasource.Change(bookmarkResource, gResourceMap[part],
  1631.               currentValue, gRdfService.GetLiteral(args[part]), true);
  1632.           } else {
  1633.             this._fileDatasource.Assert(bookmarkResource, gResourceMap[part],
  1634.               gRdfService.GetLiteral(args[part]), true);
  1635.           }
  1636.           isChanged = true;
  1637.         }
  1638.       }
  1639.  
  1640.       //check for deleted shortcuturls
  1641.       if (!args.shortcut) {
  1642.         var shortcutRsrc  = this._fileDatasource.GetTarget(bookmarkResource, grscShortcut, true);
  1643.         if (shortcutRsrc) {
  1644.           this._fileDatasource.Unassert(bookmarkResource, grscShortcut, shortcutRsrc);
  1645.         }
  1646.       }
  1647.       
  1648.       var isFeed = false;
  1649.  
  1650.       if (args["tags"]) {
  1651.         var paramTags = [];
  1652.         try {
  1653.           var tags = args["tags"];
  1654.           tags = tags.QueryInterface(CI.nsIArray).enumerate();
  1655.           while (tags.hasMoreElements()) {
  1656.             paramTags.push(tags.getNext().
  1657.               QueryInterface(CI.nsISupportsString).data);
  1658.           }
  1659.         } catch (e) {
  1660.           paramTags = args["tags"];
  1661.         }
  1662.  
  1663.         isFeed = (ybookmarksUtils.containsTag(paramTags.join(' '),
  1664.           "firefox:rss") >= 0) ? true : false;
  1665.  
  1666.         this._removeAllTagsForResource(bookmarkResource);
  1667.         this.addTag(paramTags.length, paramTags, aUrl, false);
  1668.         isChanged = true;
  1669.       }
  1670.  
  1671.       if (isChanged) {
  1672.         var modDate = (new Date()).getTime() * 1000;
  1673.         this._setDateProperty(bookmarkResource, grscLastModifiedDate, modDate);
  1674.         this._scheduleFlush();
  1675.       }
  1676.  
  1677.       if (isFeed && this._feedsRoot.IndexOf(bookmarkResource) == -1) {
  1678.         this._feedsRoot.AppendElement(bookmarkResource);
  1679.       }
  1680.       this._updateLivemarkChildren(bookmarkResource);
  1681.       return true;
  1682.     } catch (e) {
  1683.       logError("editBookmark", e);
  1684.     } finally {
  1685.       traceOut("editBookmark");
  1686.     }
  1687.     return false;
  1688.   },
  1689.  
  1690.   /**
  1691.    * Delete the bookmark and all its associated tags from the database.
  1692.    * If same tag is used by multiple bookmarks, only the association
  1693.    * between bookmark and the tag is removed. Tag is
  1694.    * retained in the system.
  1695.    *
  1696.    * @param aUrl url to be removed from the system.
  1697.    *
  1698.    * @return false if aUrl is not present in the system, true otherwise.
  1699.    *
  1700.    */
  1701.   deleteBookmark : function(aUrl) {
  1702.     traceIn("deleteBookmark");
  1703.     try {
  1704.       var bookmarkResource = this._getBookmarkResource(aUrl, false);
  1705.       if (!bookmarkResource) {
  1706.         return true;
  1707.       }
  1708.  
  1709.       this._fileDatasource.beginUpdateBatch();
  1710.       this._deleteAllChildren(bookmarkResource);
  1711.       this._removeFromBookmarksRoot(bookmarkResource);
  1712.  
  1713.       var allTagResources = this._getTagRoot().GetElements();
  1714.       while (allTagResources.hasMoreElements()) {
  1715.         var tagResource = allTagResources.getNext();
  1716.         if (this._removeMatchingContent(tagResource, bookmarkResource)) {
  1717.           this._changeTagTotalChildrenCounter(tagResource, false);
  1718.         }
  1719.       }
  1720.  
  1721.       allTagResources = this._favoriteTagsRoot.GetElements();
  1722.       while (allTagResources.hasMoreElements()) {
  1723.         tagResource = allTagResources.getNext();
  1724.         this._removeMatchingContent(tagResource, bookmarkResource);
  1725.       }
  1726.  
  1727.       // remove the bookmark from the feed container if present
  1728.       var index = this._feedsRoot.IndexOf(bookmarkResource);
  1729.       if (index != -1) {
  1730.         this._feedsRoot.RemoveElementAt(index, false);
  1731.       }
  1732.  
  1733.       this._fileDatasource.endUpdateBatch();
  1734.       this._scheduleFlush();
  1735.  
  1736.       var os = Components.classes["@mozilla.org/observer-service;1"].
  1737.                   getService(Components.interfaces.nsIObserverService);
  1738.       var notifyData = aUrl;
  1739.       os.notifyObservers(null, "ybookmark.bookmarkDeleted", notifyData);
  1740.       
  1741.       return true;
  1742.     } catch (e) {
  1743.       logError("deleteBookmark", e);
  1744.     } finally {
  1745.       traceOut("deleteBookmark");
  1746.     }
  1747.     return false;
  1748.   },
  1749.  
  1750.   getBookmark : function (aUrl) {
  1751.     traceIn("getBookmark");
  1752.     try {
  1753.       var bookmarkResource = this._getBookmarkResource(aUrl, false);
  1754.       if (!bookmarkResource) {
  1755.         return null;
  1756.       }
  1757.       return this.getBookmarkFromResource(bookmarkResource);
  1758.     } catch (e) {
  1759.       logError("getBookmark", e);
  1760.     } finally {
  1761.       traceOut("getBookmark");
  1762.     }
  1763.     return null;
  1764.   },
  1765.  
  1766.   getBookmarkFromResource : function (aBookmarkResource) {
  1767.     traceIn("getBookmarkFromResource");
  1768.     try {
  1769.       var name = this._getTargetLiteral(aBookmarkResource, grscName);
  1770.       var title = name;
  1771.       var url = this._getTargetLiteral(aBookmarkResource, grscUrl);
  1772.       var desc = this._getTargetLiteral(aBookmarkResource, grscDesc);
  1773.       var charset = this._getTargetLiteral(aBookmarkResource, grscCharset);
  1774.       if (charset == "") {
  1775.           charset = "UTF-8";
  1776.       }
  1777.       var icon = this._getTargetLiteral(aBookmarkResource, grscIcon);
  1778.       var type = this._getTargetResource(aBookmarkResource, grscType);
  1779.       var modifiedDate = this._getTargetDate(aBookmarkResource,
  1780.           grscLastModifiedDate);
  1781.       var addDate = this._getTargetDate(aBookmarkResource, grscAddDate);
  1782.       var visitDate = this._getTargetDate(aBookmarkResource, grscLastVisitDate);
  1783.       var visitCount = this._getTargetInt(aBookmarkResource, grscVisitCount);
  1784.       var shared = this._getTargetLiteral(aBookmarkResource, grscShared);
  1785.       var localOnly = this._getTargetLiteral(aBookmarkResource, grscLocalOnly);
  1786.       var shortcut = this._getTargetLiteral(aBookmarkResource, grscShortcut);
  1787.       var postData = this._getTargetLiteral(aBookmarkResource, grscPostData);
  1788.  
  1789.       var tags = this._getTargetLiterals(aBookmarkResource, grscTag);
  1790.       var id = aBookmarkResource.QueryInterface(CI.nsIRDFResource).Value;
  1791.  
  1792.       return {
  1793.         id : id,
  1794.         name : name,
  1795.         title : title,
  1796.         url : url,
  1797.         type : type,
  1798.         description : desc,
  1799.         charset : charset,
  1800.         last_visited : visitDate,
  1801.         last_modified : modifiedDate,
  1802.         added_date : addDate,
  1803.         visit_count : visitCount,
  1804.         tags : tags,
  1805.         icon : icon,
  1806.         shortcut : shortcut,
  1807.         postData : postData,
  1808.         shared : shared,
  1809.         localOnly : localOnly
  1810.       };
  1811.     } catch (e) {
  1812.       logError("getBookmarkFromResource", e);
  1813.     } finally {
  1814.       traceOut("getBookmarkFromResource");
  1815.     }
  1816.     return null;
  1817.   },
  1818.  
  1819.   _getLivemarkBookmark : function(resource) {
  1820.     var title = this._getTargetLiteral2(this._datasource, resource, grscName);
  1821.     var url = this._getTargetLiteral2(this._datasource, resource, grscUrl);
  1822.     return {
  1823.       name : title,
  1824.       title : title,
  1825.       url : url
  1826.     };
  1827.   },
  1828.  
  1829.   getBookmarksForLivemark : function(aUrl, aCount) {
  1830.     traceIn("getBookmarksForLivemark");
  1831.     try {
  1832.       var out = [];
  1833.       var livemarkResource = this._getBookmarkResource(aUrl, false);
  1834.       if (livemarkResource) {
  1835.         var livemarkContainer = gRdfContainerUtils.MakeSeq(this._datasource,
  1836.           livemarkResource);
  1837.         var bookmarks = livemarkContainer.GetElements();
  1838.         while (bookmarks.hasMoreElements()) {
  1839.           var bm = bookmarks.getNext();
  1840.           var bookmark = this._getLivemarkBookmark(bm);
  1841.           if (bookmark) {
  1842.             out.push(bookmark);
  1843.           }
  1844.         }
  1845.       }
  1846.  
  1847.       if (aCount) {
  1848.         aCount.value = out.length;
  1849.       }
  1850.       return out;
  1851.     } catch (e) {
  1852.       logError("getBookmarksForLivemark", e);
  1853.     } finally {
  1854.       traceOut("getBookmarksForLivemark");
  1855.     }
  1856.     return null;
  1857.   },
  1858.   
  1859.   /**
  1860.    * Get the total number of bookmarks
  1861.    *
  1862.    * @return the number of bookmarks
  1863.    */
  1864.   getTotalBookmarks : function() {
  1865.       return this._getBookmarkRoot().GetCount();
  1866.   },
  1867.  
  1868.   /**
  1869.    * Delete all bookmarks if the shouldDeleteAllBookmarks flag is true
  1870.    * bug fixed : cannot remove bookmarks when the xpcom starts
  1871.    */
  1872.   deleteAllBookmarks : function(shouldFlush) {
  1873.     traceIn("deleteAllBookmarks");
  1874.     try {
  1875.       if (this._allowDeleteAllBookmarks) {
  1876.         this._fileDatasource.beginUpdateBatch();
  1877.         var os = CC["@mozilla.org/observer-service;1"].
  1878.                     getService(CI.nsIObserverService);
  1879.         var notifyData = "remove-extra";  // FIXME: Is the name okay?
  1880.         os.notifyObservers(null, "ybookmark.syncBegin", notifyData);
  1881.  
  1882.         this._deleteAllContentsAndChildren(gRdfContainerUtils.MakeSeq(this._fileDatasource,
  1883.           grscBookmarksRoot));
  1884.         this._deleteAllContentsAndChildren(this._getTagRoot());
  1885.         emptyContainer(this._feedsRoot);
  1886.  
  1887.         var favTags = this._favoriteTagsRoot.GetElements();
  1888.         while (favTags.hasMoreElements()) {
  1889.           var favTag = favTags.getNext();
  1890.           favTag.QueryInterface(CI.nsIRDFResource);
  1891.           var favTagContainer = gRdfContainerUtils.MakeSeq(this._fileDatasource,
  1892.             favTag);
  1893.           emptyContainer(favTagContainer);
  1894.         }
  1895.  
  1896.         //cleanup things
  1897.         this.removeAllTransactions(10);
  1898.         this.setLastUpdateTime("");
  1899.  
  1900.         this._fileDatasource.endUpdateBatch();
  1901.         os.notifyObservers(null, "ybookmark.syncDone", notifyData);
  1902.  
  1903.         if (shouldFlush) {
  1904.           this._scheduleFlush();
  1905.         }
  1906.       }
  1907.     } catch (e) {
  1908.       logError("deleteAllBookmarks", e);
  1909.       this._allowDeleteAllBookmarks = false;
  1910.     } finally {
  1911.       traceOut("deleteAllBookmarks");
  1912.     }
  1913.   },
  1914.  
  1915.   /*********************************************************************
  1916.    *                      Bookmark Tag Functions                       *
  1917.    *********************************************************************/
  1918.  
  1919.   addTag : function(aCountTags, aTags, aUrl, shouldFlush) {
  1920.     traceIn("addTag");
  1921.     try {
  1922.       var bookmarkResource = this._getBookmarkResource(aUrl, false);
  1923.       if ( !bookmarkResource ) {
  1924.         yDebug.print("addTag: " + aUrl + " is not in the bookmark database",
  1925.           YB_LOG_MESSAGE );
  1926.         return false;
  1927.       }
  1928.       this._addTags(bookmarkResource, aTags);
  1929.  
  1930.       if (shouldFlush) {
  1931.         this._scheduleFlush();
  1932.       }
  1933.  
  1934.       return true;
  1935.     } catch (e) {
  1936.       logError("addTag", e);
  1937.     } finally {
  1938.       traceOut("addTag");
  1939.     }
  1940.   },
  1941.  
  1942.   /**
  1943.    * Get the tags for a given url. If url parameter is null, return all
  1944.    * the tags in the system.
  1945.    *
  1946.    * @param aUrl url for which tags are requested.
  1947.    * @param aCount parameter which will be set to the number of items in
  1948.    * the returned array.
  1949.    *
  1950.    * @return an array of string having the tags for the given aUrl.
  1951.    * An empty array if aUrl is not present in the system or aUrl do not
  1952.    * have any tags.
  1953.    *
  1954.    */
  1955.   getTags : function(aUrl, aCount) {
  1956.     traceIn("getTags");
  1957.     var out = new Array();
  1958.     try {
  1959.       if (aUrl) {
  1960.         var bookmarkResource = this._getBookmarkResource(aUrl, false);
  1961.         if (bookmarkResource) {
  1962.           var tags = this._fileDatasource.GetTargets(bookmarkResource,
  1963.             grscTag, true);
  1964.           while (tags.hasMoreElements()) {
  1965.             var value = tags.getNext().QueryInterface(CI.nsIRDFLiteral).Value;
  1966.             out.push(value);
  1967.           }
  1968.         }
  1969.       } else {
  1970.         var tags = this._getTagRoot().GetElements();
  1971.         while (tags.hasMoreElements()) {
  1972.           var tagValue = this._fileDatasource.GetTarget(tags.getNext(),
  1973.             grscTagValue, true).QueryInterface(CI.nsIRDFLiteral).Value;
  1974.           out.push(tagValue);
  1975.         }
  1976.       }
  1977.       aCount.value = out.length;
  1978.     } catch (e) {
  1979.       logError("getTags", e);
  1980.     } finally {
  1981.       traceOut("getTags");
  1982.     }
  1983.     return out;
  1984.   },
  1985.  
  1986.   /*********************************************************************
  1987.    *                              Tag Functions                        *
  1988.    *********************************************************************/
  1989.  
  1990.   /*
  1991.    * Get the total number of tags
  1992.    *
  1993.    * @return the number of tags
  1994.    */
  1995.   getTotalTags : function() {
  1996.       return this._getTagRoot().GetCount();
  1997.   },
  1998.  
  1999.   /**
  2000.    * Get the total number of bookmarks for tag
  2001.    *
  2002.    * @return the number of bookmarks
  2003.    */
  2004.   getTotalBookmarksForTag : function(aTag) {
  2005.     var tagResource = this._getTagResource(aTag, false);
  2006.  
  2007.     if (tagResource) {
  2008.       var childCounter = this._fileDatasource.GetTarget(tagResource,
  2009.         grscChildCount, true);
  2010.       if (childCounter) {
  2011.         childCounter.QueryInterface(CI.nsIRDFInt);
  2012.         return childCounter.Value;
  2013.       } else {
  2014.         return 0;
  2015.       }
  2016.     }
  2017.   },
  2018.  
  2019.   /**
  2020.    * Returned tag resource name can be used in the ref attribute of the
  2021.    * template in XUL.
  2022.    *
  2023.    * @param aTag tag for which tag resource name is requested
  2024.    *
  2025.    * @return the resource name used (anonymous) for the tag in the local store.
  2026.    */
  2027.   getTagResourceName : function (aTag) {
  2028.     traceIn("getTagResourceName");
  2029.     try {
  2030.       var tagResource = this._getTagResource(aTag, false);
  2031.       if (tagResource) {
  2032.         return tagResource.QueryInterface(CI.nsIRDFResource).Value;
  2033.       } else {
  2034.         return null;
  2035.       }
  2036.     } catch (e) {
  2037.       logError("getTagResourceName", e);
  2038.     } finally {
  2039.       traceOut("getTagResourceName");
  2040.     }
  2041.   },
  2042.  
  2043.   /*********************************************************************
  2044.    *                           Private Functions                       *
  2045.    *********************************************************************/
  2046.  
  2047.   /** Deletes all predicates and objects of the given subject */
  2048.   _deleteAllChildren : function(parent) {
  2049.     var children = this._fileDatasource.ArcLabelsOut(parent);
  2050.     while (children.hasMoreElements()) {
  2051.       var predicate = children.getNext();
  2052.       predicate.QueryInterface(CI.nsIRDFResource);
  2053.       var objects = this._fileDatasource.GetTargets(parent, predicate, true);
  2054.       while (objects.hasMoreElements()) {
  2055.         this._fileDatasource.Unassert(parent, predicate, objects.getNext());
  2056.       }
  2057.     }
  2058.   },
  2059.  
  2060.   /** Deletes all contents and their children for the given container */
  2061.   _deleteAllContentsAndChildren : function(container) {
  2062.       var contents = container.GetElements();
  2063.       while (contents.hasMoreElements()) {
  2064.         var content = contents.getNext();
  2065.         this._deleteAllChildren(content);
  2066.         container.RemoveElement(content, false);
  2067.       }
  2068.   },
  2069.  
  2070.   /** Removes a bookmark resource from bookmarks root */
  2071.   _removeFromBookmarksRoot : function(bookmarkResource) {
  2072.     if (bookmarkResource) {
  2073.       this._getBookmarkRoot().RemoveElement(bookmarkResource, false);
  2074.     }
  2075.   },
  2076.  
  2077.   /** Removes a tag resource from tags root */
  2078.   _removeFromTagRoot : function (tagResource) {
  2079.     if (tagResource) {
  2080.       this._getTagRoot().RemoveElement(tagResource, false);
  2081.     }
  2082.   },
  2083.  
  2084.   /** Removes a tag resource from tags root */
  2085.   _removeFromBundlesRoot : function (bnResource) {
  2086.     if (bnResource) {
  2087.       this._bundleRoot.RemoveElement(bnResource, true);
  2088.     }
  2089.   },
  2090.  
  2091.   /**
  2092.    * Walks through the contents of the given resource and removes
  2093.    * the content that matches the given one.
  2094.    */
  2095.   _removeMatchingContent : function(resource, content) {
  2096.     var container = gRdfContainerUtils.MakeSeq(this._fileDatasource, resource);
  2097.     var index = container.IndexOf(content);
  2098.     if (index != -1) {
  2099.       container.RemoveElementAt(index, false);
  2100.       return true;
  2101.     }
  2102.     return false;
  2103.   },
  2104.  
  2105.   _gatherContents : function (container) {
  2106.     var result = [];
  2107.     var contents = container.GetElements();
  2108.     while (contents.hasMoreElements()) {
  2109.       var content = this.getBookmarkFromResource(contents.getNext());
  2110.       if (content) {
  2111.         result.push(content);
  2112.       }
  2113.     }
  2114.     return result;
  2115.   },
  2116.  
  2117.   /**
  2118.    * Get the bookmarks for a given tag. If aTag is null,
  2119.    * return all the bookmarks.
  2120.    *
  2121.    * @param aTag tag for which bookmark is requested.
  2122.    * @param aCount parameter which will be set to the number of items
  2123.    * in the returned array.
  2124.    * @return an array of nsIYBookmark object. Each object is a key value pair.
  2125.    * An empty array if no urls tagged with the given tag or aTag is not
  2126.    * present in the database.
  2127.    *
  2128.    */
  2129.   getBookmarks : function(aTag, aCount) {
  2130.     traceIn("getBookmarks");
  2131.     var result = new Array();
  2132.     try {
  2133.       if (aTag) {
  2134.         var tagResource = this._getTagResource(aTag);
  2135.         if (tagResource) {
  2136.           result = this._gatherContents(
  2137.             contents = gRdfContainerUtils.MakeSeq(this._fileDatasource, tagResource));
  2138.         }
  2139.       } else {
  2140.         result = this._gatherContents(this._getBookmarkRoot());
  2141.       }
  2142.  
  2143.       if (aCount) {
  2144.         aCount.value = result.length;
  2145.       }
  2146.     } catch (e) {
  2147.       logError("getBookmarks", e);
  2148.     } finally {
  2149.       traceOut("getBookmarks");
  2150.     }
  2151.  
  2152.     return result;
  2153.   },
  2154.  
  2155.  
  2156.   _setAddDate : function (aUrl, aDate) {
  2157.     this._setDateProperty(aUrl, grscAddDate, aDate);
  2158.   },
  2159.  
  2160.   _getTargetLiteral2 : function(datasource, source, predicate) {
  2161.     var target = datasource.GetTarget(source, predicate, true);
  2162.     if (target) {
  2163.       return target.QueryInterface(CI.nsIRDFLiteral).Value;
  2164.     } else {
  2165.       //yDebug.print("Predicate '" + predicate.Value + "' not found for '" + source.Value + "'");
  2166.       return "";
  2167.     }
  2168.   },
  2169.  
  2170.   _getTargetLiteral : function(source, predicate) {
  2171.     return this._getTargetLiteral2(this._fileDatasource, source, predicate);
  2172.   },
  2173.  
  2174.   _getTargetBoolean : function(source, predicte) {
  2175.     return (this._getTargetLiteral(source, predicte) == "true") ? true : false;
  2176.   },
  2177.  
  2178.   _getTargetDate : function(source, predicate) {
  2179.     var target = this._fileDatasource.GetTarget(source, predicate, true);
  2180.     if (target) {
  2181.       return target.QueryInterface(CI.nsIRDFDate).Value;
  2182.     } else {
  2183.       return 0;
  2184.     }
  2185.   },
  2186.  
  2187.   _getTargetInt : function(source, predicate) {
  2188.     var target = this._fileDatasource.GetTarget(source, predicate, true);
  2189.     if (target) {
  2190.       return target.QueryInterface(CI.nsIRDFInt).Value;
  2191.     } else {
  2192.       return 0;
  2193.     }
  2194.   },
  2195.  
  2196.   _getTargetResource : function(source, predicate) {
  2197.     var target = this._fileDatasource.GetTarget(source, predicate, true);
  2198.     if (target) {
  2199.       return target.QueryInterface(CI.nsIRDFResource).Value;
  2200.     } else {
  2201.       return "";
  2202.     }
  2203.   },
  2204.  
  2205.   _getTargetLiterals : function(source, predicate) {
  2206.     var t = this._fileDatasource.GetTargets(source, predicate, true);
  2207.     var result = new NSArray();
  2208.     while (t.hasMoreElements()) {
  2209.       var value = t.getNext().QueryInterface(CI.nsIRDFLiteral).Value;
  2210.       var nsValue = new NSString();
  2211.       nsValue.data = value;
  2212.       result.appendElement(nsValue, false);
  2213.     }
  2214.     return result.QueryInterface(CI.nsIArray);
  2215.   },
  2216.  
  2217.   /**
  2218.    *  Get the type of a resource
  2219.    *
  2220.    *  @param aResource a bookmark resource
  2221.    *
  2222.    *  @return string the type of the resource
  2223.    */
  2224.   resolveBookmarkResourceType : function (aResource) {
  2225.     traceIn("resolveBookmarkResourceType");
  2226.     try {
  2227.       var t = this._datasource.GetTarget(aResource, grscType, true);
  2228.       return (t == grscLiveBookmarkType) ? "LiveBookmark" 
  2229.         : ((t == grscLivemarkType) ? "Livemark" : 
  2230.         ((t == grscBookmarkType) ? "Bookmark" : ""));
  2231.     } catch (e) {
  2232.       logError("resolveBookmarkResourceType", e);
  2233.     } finally {
  2234.       traceOut("resolveBookmarkResourceType");
  2235.     }
  2236.     return "";
  2237.   },
  2238.  
  2239.   /**
  2240.    * Whenever website is visited via the bookmark link the count on the
  2241.    * bookmark is increased.
  2242.    * This should also update the last visited time (if maintained).
  2243.    *
  2244.    * @param aUrl url visited
  2245.    */
  2246.   incrementVisitCount : function(aUrl) {
  2247.     traceIn("incrementVisitCount");
  2248.     try {
  2249.       var bookmarkResource = this._getBookmarkResource(aUrl, false);
  2250.       if (bookmarkResource) {
  2251.         var count = this._fileDatasource.GetTarget(bookmarkResource,
  2252.           grscVisitCount, true);
  2253.         if (count) {
  2254.           count.QueryInterface(CI.nsIRDFInt);
  2255.           this._fileDatasource.Change(bookmarkResource, grscVisitCount,
  2256.             count, gRdfService.GetIntLiteral(count.Value + 1), true);
  2257.         } else {
  2258.           this._fileDatasource.Assert(bookmarkResource, grscVisitCount,
  2259.             gRdfService.GetIntLiteral(1), true);
  2260.         }
  2261.         var nowDate = (new Date()).getTime() * 1000;
  2262.         this._setDateProperty(bookmarkResource, grscLastVisitDate, nowDate);
  2263.         this._scheduleFlush();
  2264.       }
  2265.     } catch (e) {
  2266.       logError("incrementVisitCount", e);
  2267.     } finally {
  2268.       traceOut("incrementVisitCount");
  2269.     }
  2270.   },
  2271.  
  2272.   /**
  2273.    * Returns the source for the given predicate and value.
  2274.    */
  2275.   _getSource : function(predicate, value) {
  2276.     return this._fileDatasource.GetSource(predicate, value, true);
  2277.   },
  2278.   /**
  2279.    * Check if the given url is present in the bookmark database.
  2280.    *
  2281.    * @param aUrl url of the bookmark to be checked.
  2282.    *
  2283.    * @return bookmark resource if present, null otherwise.
  2284.    *
  2285.    */
  2286.   isBookmarked : function(aUrl) {
  2287.     traceIn("isBookmarked");
  2288.     try {
  2289.       return this._getBookmarkResource(aUrl, false);
  2290.     } catch (e) {
  2291.       logError("isBookmarked", e);
  2292.     } finally {
  2293.       traceOut("isBookmarked");
  2294.     }
  2295.     return null;
  2296.   },
  2297.  
  2298.   /* Check if the given feed url is present in the bookmark database.
  2299.    *
  2300.    * @param aFeedUrl url of the livemark to be checked.
  2301.    *
  2302.    * @return bookmark resource if present, null otherwise.
  2303.    */
  2304.   isLivemarked : function(aFeedUrl) {
  2305.     traceIn("isLivemarked");
  2306.     try {
  2307.       return this._getSource(grscFeedUrl, gRdfService.GetLiteral(aFeedUrl));
  2308.     } catch (e) {
  2309.       logError("isLivemarked", e);
  2310.     } finally {
  2311.       traceOut("isLivemarked");
  2312.     }
  2313.     return null;
  2314.   },
  2315.  
  2316.   /*********************************************************************
  2317.    *                     Setter and Getter Functions                   *
  2318.    *********************************************************************/
  2319.  
  2320.   setBookmarkKeyAsString : function(aUrl, aKey, aValue) {
  2321.     try {
  2322.       traceIn("setBookmarkKeyAsString");
  2323.       this._setBookmarkKey(aUrl, aKey, gRdfService.GetLiteral(aValue));
  2324.     } catch (e) {
  2325.       logError("setBookmarkKeyAsString", e);
  2326.     } finally {
  2327.       traceOut("setBookmarkKeyAsString");
  2328.     }
  2329.   },
  2330.  
  2331.   setBookmarkKeyAsDate : function(aUrl, aKey, aValue) {
  2332.     traceIn("setBookmarkKeyAsDate");
  2333.     try {
  2334.       this._setBookmarkKey(aUrl, aKey, gRdfService.GetDateLiteral(aValue));
  2335.     } catch (e) {
  2336.       logError("setBookmarkKeyAsDate", e);
  2337.     }
  2338.   },
  2339.  
  2340.   setBookmarkKeyAsInt : function(aUrl, aKey, aValue) {
  2341.     traceIn("setBookmarkKeyAsInt");
  2342.     try {
  2343.       this._setBookmarkKey(aUrl, aKey, gRdfService.GetIntLiteral(aValue));
  2344.     } catch (e) {
  2345.       logError("setBookmarkKeyAsInt", e);
  2346.     } finally {
  2347.       traceOut("setBookmarkKeyAsInt");
  2348.     }
  2349.   },
  2350.  
  2351.   setBookmarkKeysAsString : function(aUrl, aLength, aKeys, aValues) {
  2352.     traceIn("setBookmarkKeysAsString");
  2353.     try {
  2354.       var bookmarkResource = this._getBookmarkResource(aUrl, false);
  2355.       if (bookmarkResource) {
  2356.         this._checkKeyAndValues(aKeys, aValues, aLength);
  2357.         for (var counter = 0; counter < aLength; ++counter) {
  2358.           this._setBookmarkResourceKey(bookmarkResource, aKeys[counter],
  2359.             gRdfService.GetLiteral(aValues[counter]));
  2360.         }
  2361.       }
  2362.     } catch (e) {
  2363.       logError("setBookmarkKeysAsString", e);
  2364.     } finally {
  2365.       traceOut("setBookmarkKeysAsString");
  2366.     }
  2367.   },
  2368.  
  2369.   setBookmarkKeysAsDate : function(aUrl, aLength,  aKeys, aValues) {
  2370.     traceIn("setBookmarkKeysAsDate");
  2371.     try {
  2372.       var bookmarkResource = this._getBookmarkResource(aUrl, false);
  2373.       if (bookmarkResource) {
  2374.         this._checkKeyAndValues(aKeys, aValues, aLength);
  2375.         for (var counter = 0; counter < aLength; ++counter) {
  2376.           this._setBookmarkResourceKey(bookmarkResource, aKeys[counter],
  2377.             gRdfService.GetDateLiteral(aValues[counter]));
  2378.         }
  2379.       }
  2380.     } catch (e) {
  2381.       logError("setBookmarkKeysAsDate", e);
  2382.     } finally {
  2383.       traceOut("setBookmarkKeysAsDate");
  2384.     }
  2385.   },
  2386.  
  2387.   setBookmarkKeysAsInt : function(aUrl, aLength,  aKeys, aValues) {
  2388.     traceIn("setBookmarkKeysAsInt");
  2389.     try {
  2390.       var bookmarkResource = this._getBookmarkResource(aUrl, false);
  2391.       if (bookmarkResource) {
  2392.         this._checkKeyAndValues(aKeys, aValues, aLength);
  2393.         for (var counter = 0; counter < aLength; ++counter) {
  2394.           this._setBookmarkResourceKey(bookmarkResource, aKeys[counter],
  2395.             gRdfService.GetIntLiteral(aValues[counter]));
  2396.         }
  2397.       }
  2398.     } catch (e) {
  2399.       logError("setBookmarkKeysAsInt", e);
  2400.     } finally {
  2401.       traceOut("setBookmarkKeysAsInt");
  2402.     }
  2403.   },
  2404.  
  2405.   getBookmarkStringValues : function(aUrl, aInCount, aKeys, aOutCount) {
  2406.     traceIn("getBookmarkStringValues");
  2407.     try {
  2408.       return this._getBookmarkKeyValues(aUrl, aInCount, aKeys, aOutCount,
  2409.         Components.interfaces.nsIRDFLiteral);
  2410.     } catch (e) {
  2411.       logError("getBookmarkStringValues", e);
  2412.     } finally {
  2413.       traceOut("getBookmarkStringValues");
  2414.     }
  2415.   },
  2416.  
  2417.   getBookmarkIntValues : function(aUrl, aInCount, aKeys, aOutCount) {
  2418.     traceIn("getBookmarkIntValues");
  2419.     try {
  2420.       return this._getBookmarkKeyValues(aUrl, aInCount, aKeys, aOutCount,
  2421.         Components.interfaces.nsIRDFInt);
  2422.     } catch (e) {
  2423.       logError("getBookmarkIntValues", e);
  2424.     } finally {
  2425.       traceOut("getBookmarkIntValues");
  2426.     }
  2427.   },
  2428.  
  2429.   getBookmarkDateValues : function(aUrl, aInCount, aKeys, aOutCount) {
  2430.     traceIn("getBookmarkDateValues");
  2431.     try {
  2432.       return this._getBookmarkKeyValues(aUrl, aInCount, aKeys, aOutCount,
  2433.         Components.interfaces.nsIRDFDate);
  2434.     } catch (e) {
  2435.       logError("getBookmarkDateValues", e);
  2436.     } finally {
  2437.       traceOut("getBookmarkDateValues");
  2438.     }
  2439.   },
  2440.  
  2441.   _getBookmarkKeyValues : function(aUrl, aInCount, aKeys, aOutCount,
  2442.     aInterface) {
  2443.     bookmarkResource = this._getBookmarkResource(aUrl, false);
  2444.     if (!bookmarkResource) {
  2445.       aOutCount.value = 0;
  2446.       return [];
  2447.     }
  2448.  
  2449.     if (aKeys.length < aInCount) {
  2450.       throw Components.Exception(
  2451.         "Keys is not having enough elements. Expected: " + aInCount + " Got: "
  2452.           + aKeys.length);
  2453.     }
  2454.  
  2455.     var outArray = new Array();
  2456.  
  2457.     for (var counter = 0; counter < aInCount; ++counter) {
  2458.       outArray.push(this._getProperty(bookmarkResource,
  2459.         gRdfService.GetResource(NS_BOOKMARK_BASE + aKeys[counter]),
  2460.           aInterface));
  2461.     }
  2462.     aOutCount.value = outArray.length;
  2463.     return outArray;
  2464.   },
  2465.  
  2466.   _getProperty : function(resource, predicate, aInterface) {
  2467.     var value = this._fileDatasource.GetTarget(resource, predicate, true);
  2468.     if (! value) {
  2469.       yDebug.print("value for " + predicate.Value +
  2470.         " not found. Setting it to null");
  2471.       return null;
  2472.     } else {
  2473.       value.QueryInterface(aInterface);
  2474.       return value.Value;
  2475.     }
  2476.   },
  2477.  
  2478.   setLastUpdateTime : function(aTimeString) {
  2479.     traceIn("setLastUpdateTime");
  2480.     try {
  2481.       this._setStringProperty(this._getBookmarkRoot().Resource,
  2482.         grscLastUpdateDate, aTimeString);
  2483.       this._scheduleFlush();
  2484.     } catch (e) {
  2485.       logError("setLastUpdateTime", e);
  2486.     } finally {
  2487.       traceOut("setLastUpdateTime");
  2488.     }
  2489.   },
  2490.  
  2491.   getLastUpdateTime : function() {
  2492.     traceIn("getLastUpdateTime");
  2493.     try {
  2494.       return this._getProperty(this._getBookmarkRoot().Resource,
  2495.         grscLastUpdateDate, CI.nsIRDFLiteral);
  2496.     } catch (e) {
  2497.       logError("getLastUpdateTime", e);
  2498.     } finally {
  2499.       traceOut("getLastUpdateTime");
  2500.     }
  2501.   },
  2502.  
  2503.  setFeedLastUpdateTime : function(feedURL, aTimeString) {
  2504.     var target = gRdfService.GetLiteral(feedURL);
  2505.     var resource = this._getSource(grscDelFeedUrl, target);
  2506.     if(!resource) {
  2507.         var resource = gRdfService.GetAnonymousResource();
  2508.         this._setStringProperty(resource, grscDelFeedLastUpdate, aTimeString);
  2509.         this._setStringProperty(resource, grscDelFeedUrl, feedURL);
  2510.          this._setProperty(resource, grscType, grscDelFeedType);
  2511.         this._DeliciousFeedsRoot.AppendElement(resource);
  2512.     } else { 
  2513.         this._setStringProperty(resource, grscDelFeedLastUpdate, aTimeString);        
  2514.     }        
  2515.     this._doFlush();
  2516.   },
  2517.  
  2518.  getFeedLastUpdateTime : function(feedURL) {   
  2519.     
  2520.     var target = gRdfService.GetLiteral(feedURL);
  2521.     var source = this._getSource(grscDelFeedUrl, target);
  2522.     if(source) {         
  2523.         return this._getTargetValue(source, grscDelFeedLastUpdate);
  2524.     }    
  2525.     return null;
  2526.   },
  2527.   
  2528.   _checkKeyAndValues : function(aKeys, aValues, aLength) {
  2529.     if ((aKeys.length < aLength) || (aValues.length < aLength)) {
  2530.       throw Components.Exception("Either keys or values are not having "
  2531.         + aLength + " number of items");
  2532.     }
  2533.   },
  2534.  
  2535.   _setBookmarkResourceKey : function(bookmarkResource, aKey, newValue) {
  2536.     this._setProperty(bookmarkResource,
  2537.       gRdfService.GetResource(NS_BOOKMARK_BASE + aKey), newValue);
  2538.     this._scheduleFlush();
  2539.   },
  2540.  
  2541.   _setBookmarkKey : function (aUrl, aKey, newValue) {
  2542.     var bookmarkResource = this._getBookmarkResource(aUrl, false);
  2543.     if (bookmarkResource) {
  2544.       this._setBookmarkResourceKey(bookmarkResource, aKey, newValue);
  2545.     }
  2546.   },
  2547.  
  2548.   /**
  2549.    *  Create a new empty _datasource which would be used to hold the
  2550.    *  search results
  2551.    */
  2552.   getSearchDataSource : function () {
  2553.     traceIn("getSearchDataSource");
  2554.     try {
  2555.       return this._searchDatasource;
  2556.     } catch (e) {
  2557.       logError("getSearchDataSource", e);
  2558.     } finally {
  2559.       traceOut("getSearchDataSource");
  2560.     }
  2561.   },
  2562.  
  2563.   /**
  2564.    * Create a new container to store search results if it does not exist
  2565.    * in the search _datasource @param aStoreNumber number to identify the
  2566.    * store e.g 1 for sidebar,  2 for popup
  2567.    */
  2568.   _createSearchStore : function (aStoreNumber) {
  2569.     var searchRootRes = gRdfService.GetResource("NC:YBSearch" + aStoreNumber);
  2570.     var tagSearchRootRes = gRdfService.GetResource("NC:YBTagSearch"
  2571.       + aStoreNumber);
  2572.     var bookmarkSearchRootRes = gRdfService.GetResource("NC:YBBookmarkSearch" +
  2573.       aStoreNumber);
  2574.  
  2575.     var tagSearchRoot = gRdfContainerUtils.MakeSeq(this._searchDatasource,
  2576.       tagSearchRootRes);
  2577.     var bookmarkSearchRoot = gRdfContainerUtils.MakeSeq(this._searchDatasource,
  2578.       bookmarkSearchRootRes);
  2579.  
  2580.     if (!gRdfContainerUtils.IsSeq(this._searchDatasource, searchRootRes)) {
  2581.       var searchRoot = gRdfContainerUtils.MakeSeq(this._searchDatasource,
  2582.         searchRootRes);
  2583.       searchRoot.AppendElement(tagSearchRootRes);
  2584.       searchRoot.AppendElement(bookmarkSearchRootRes);
  2585.     }
  2586.  
  2587.     return {
  2588.       tagSearchRoot : tagSearchRoot,
  2589.       bookmarkSearchRoot : bookmarkSearchRoot
  2590.     };
  2591.   },
  2592.  
  2593.   /**
  2594.    * Search bookmarks and tags, and set the results in the search _datasource
  2595.    * (NC:YBSearch + aStoreNumber)
  2596.    *
  2597.    * @param aKeyword keyword to be searched
  2598.    * @param aStoreNumber number to identify the store e.g 1 for sidebar,
  2599.    * 2 for popup
  2600.    */
  2601.   search : function (aKeyword, aStoreNumber) {
  2602.     traceIn("search");
  2603.     try {
  2604.       var tagCounter = this.searchTags(aKeyword, aStoreNumber);
  2605.       var bookmarkCounter = this.searchBookmarks(aKeyword, aStoreNumber);
  2606.  
  2607.       return tagCounter + bookmarkCounter;
  2608.     } catch (e) {
  2609.       logError("search", e);
  2610.     } finally {
  2611.       traceOut("search");
  2612.     }
  2613.     return 0;
  2614.   },
  2615.  
  2616.   /**
  2617.    * Search tag folders and add them to the search _datasource
  2618.    * (NC:YBTagsSearch + aStoreNumber)
  2619.    * if their name contains the keyword
  2620.    *
  2621.    * @param aKeyword keyword to be searched
  2622.    * @param aStoreNumber number to identify the store e.g 1 for sidebar,
  2623.    * 2 for popup
  2624.    */
  2625.   searchTags : function (aKeyword, aStoreNumber) {
  2626.     traceIn("searchTags");
  2627.     try {
  2628.       if (this._tagSearchTimeout) {
  2629.         this._hiddenWindow.clearTimeout(this._tagSearchTimeout);
  2630.         this._tagSearchTimeout = null;
  2631.       } else {
  2632.         if (this._isTagSearching) {
  2633.           this._stopTagSearching = true;
  2634.           var self = this;
  2635.           this._hiddenWindow.setTimeout(function(self, aKeyword, aStoreNumber) {
  2636.                self.searchTags(aKeyword, aStoreNumber); }, 10,
  2637.                self, aKeyword, aStoreNumber);
  2638.           return 0;
  2639.         } else {
  2640.          this._stopTagSearching = false;
  2641.         }
  2642.       }
  2643.  
  2644.       this._isTagSearching = true;
  2645.       var store = this._createSearchStore(aStoreNumber);
  2646.       var tagSearchRoot = store.tagSearchRoot;
  2647.       var totalSearched = this._getTotalElementsToSearch();
  2648.  
  2649.       // Clear those left over by the previous search
  2650.       emptyContainer(tagSearchRoot);
  2651.  
  2652.       //get the new search results
  2653.       var keyword = aKeyword.toLowerCase();
  2654.       var tags = this._getTagRoot().GetElements();
  2655.       var tagResource;
  2656.       var name;
  2657.       var totalMatched = 0;
  2658.  
  2659.       var os = Components.classes["@mozilla.org/observer-service;1"].
  2660.                   getService(Components.interfaces.nsIObserverService);
  2661.  
  2662.       this._postSearchTagsBegin(os, keyword);
  2663.       this._isTagSearching = false;
  2664.       this._startSearchTagsTimer(tags, keyword, totalSearched,
  2665.         totalMatched, tagSearchRoot);
  2666.     } catch (e) {
  2667.       logError("searchTags", e);
  2668.     } finally {
  2669.       traceOut("searchTags");
  2670.     }
  2671.     return 0;
  2672.   },
  2673.  
  2674.   /**
  2675.    * searches given string for keywords. By default, it does a
  2676.    * disjunction search (returns true if str contains _ANY_ of
  2677.    * the keywords). Conjunction search can be specified using
  2678.    * the last arg
  2679.    */
  2680.   _searchStringForKeywords : function(str, keywords, conjunction) {
  2681.     if (keywords.length == 0) {
  2682.       return false;
  2683.     }
  2684.     if (conjunction == null) {
  2685.       conjunction = false;
  2686.     }
  2687.  
  2688.     str = str.toLowerCase();
  2689.     var nValidKeywords = 0, nMatches = 0;
  2690.     for (var i = 0; i < keywords.length; ++i) {
  2691.       if (keywords[i].length > 0) {
  2692.         ++nValidKeywords;
  2693.         if (str.indexOf(keywords[i]) != -1) {
  2694.           if (!conjunction) {
  2695.             return true;
  2696.           } else {
  2697.             ++nMatches;
  2698.           }
  2699.         }
  2700.       }
  2701.     }
  2702.     if (nMatches == nValidKeywords) {  // can only be conjunction
  2703.       return true;
  2704.     }
  2705.     return false;
  2706.   },
  2707.  
  2708.   _searchTags : function(tags, keyword, totalSearched, totalMatched,
  2709.     tagSearchRoot) {
  2710.     try {
  2711.       this._isTagSearching = true;
  2712.  
  2713.       var os = Components.classes["@mozilla.org/observer-service;1"].
  2714.                 getService(Components.interfaces.nsIObserverService);
  2715.       var sofarSearched = 1;
  2716.       var allTagsSearched = false;
  2717.       if (keyword.indexOf('+') != -1) {
  2718.         // user is typing a conjunction search - no results here
  2719.         allTagsSearched = true;
  2720.       }
  2721.  
  2722.       var keywordLets = keyword.split(' ');
  2723.       if (! allTagsSearched) {
  2724.         while (tags.hasMoreElements()) {
  2725.           if (sofarSearched++ % SOFAR_SEARCHED == 0) {
  2726.             this._postSearchTagsProgress(os, totalSearched,
  2727.               sofarSearched, keyword);
  2728.             break;
  2729.           }
  2730.  
  2731.           var tagResource = tags.getNext();
  2732.           var found = this._findKeywords(tagResource, grscTagValue, keywordLets);
  2733.  
  2734.           if (found) {
  2735.             if (tagSearchRoot.IndexOf(tagResource) == -1) {
  2736.               tagSearchRoot.AppendElement(tagResource);
  2737.               totalMatched++;
  2738.             }
  2739.           }
  2740.         }
  2741.         if (! tags.hasMoreElements()) {
  2742.           allTagsSearched = true;
  2743.         }
  2744.       }
  2745.  
  2746.       this._isTagSearching = false;
  2747.       if (! this._stopTagSearching) {
  2748.         if (allTagsSearched) {
  2749.           this._postTagsEnd(os, totalMatched, keyword);
  2750.         } else {
  2751.           this._startSearchTagsTimer(tags, keyword, totalSearched,
  2752.             totalMatched, tagSearchRoot);
  2753.         }
  2754.       }
  2755.     } catch (e) {
  2756.       logError("_searchTags", e);
  2757.     }
  2758.   },
  2759.  
  2760.   /**
  2761.    * ASSUMPTION: All bookmarks and tags are searched
  2762.    *
  2763.    */
  2764.   _getTotalElementsToSearch : function() {
  2765.     return this.getTotalBookmarks() + this.getTotalTags();
  2766.   },
  2767.  
  2768.   /**
  2769.    * Search bookmarks and add them to the search _datasource
  2770.    * (NC:YBBookmarksSearch + aStoreNumner)
  2771.    * if their name, url or tags contains the keyword
  2772.    *
  2773.    * @param aKeyword keyword to be searched
  2774.    * @param aStoreNumber number to identify the store e.g 1 for sidebar,
  2775.    * 2 for popup
  2776.    */
  2777.   searchBookmarks : function (aKeyword, aStoreNumber) {
  2778.     traceIn("searchBookmarks");
  2779.     try {
  2780.       if (this._bookmarkSearchTimeout) {
  2781.         this._hiddenWindow.clearTimeout(this._bookmarkSearchTimeout);
  2782.         this._bookmarkSearchTimeout = null;
  2783.       } else {
  2784.         if (this._isBookmarkSearching) {
  2785.           this._stopBookmarkSearching = true;
  2786.           var self = this;
  2787.           this._hiddenWindow.setTimeout(function(self, aKeyword, aStoreNumber) {
  2788.              self.searchBookmarks(aKeyword, aStoreNumber);
  2789.           }, 10, self, aKeyword, aStoreNumber);
  2790.           return;
  2791.         } else {
  2792.           this._stopBookmarkSearching = false;
  2793.         }
  2794.       }
  2795.  
  2796.       this._isBookmarkSearching = true;
  2797.       yDebug.print ("Searching for " + aKeyword);
  2798.       var store = this._createSearchStore(aStoreNumber);
  2799.       var bookmarkSearchRoot = store.bookmarkSearchRoot;
  2800.  
  2801.       // remove all resources created by previous search
  2802.       emptyContainer(bookmarkSearchRoot);
  2803.  
  2804.       // get the new search results
  2805.       var keyword = aKeyword.toLowerCase();
  2806.       var bookmarks = this._getBookmarkRoot().GetElements();
  2807.       var totalSearched = this._getTotalElementsToSearch();
  2808.       var totalMatched = 0;
  2809.  
  2810.       var os = Components.classes["@mozilla.org/observer-service;1"].
  2811.                   getService(Components.interfaces.nsIObserverService);
  2812.  
  2813.       this._postSearchBookmarksBegin(os, keyword);
  2814.       this._isBookmarkSearching = false;
  2815.       this._startSearchBookmarksTimer(bookmarks, keyword, 
  2816.         totalSearched, totalMatched, bookmarkSearchRoot);
  2817.     } catch (e) {
  2818.       logError("searchBookmarks", e);
  2819.     } finally {
  2820.       traceOut("searchBookmarks");
  2821.     }
  2822.     return 0;
  2823.   },
  2824.  
  2825.   getTagsSearchResults: function(aStoreNumber, aCount) {
  2826.  
  2827.     var myTags = [];
  2828.     var tagsRoot = gRdfContainerUtils.MakeSeq(this._searchDatasource, gRdfService.GetResource("NC:YBTagSearch" + aStoreNumber));
  2829.     var tags = tagsRoot.GetElements();
  2830.     var hasMoreElements = tags.hasMoreElements;
  2831.     var getNext = tags.getNext;
  2832.  
  2833.     while ( hasMoreElements() ) {
  2834.         myTags[myTags.length] = this._getTargetLiteral(getNext(), grscTagValue);
  2835.     }
  2836.  
  2837.     aCount.value = myTags.length;
  2838.     return myTags;
  2839.   },
  2840.  
  2841.   getBookmarksSearchResults: function(aStoreNumber, aCount) {
  2842.  
  2843.       var myBookmarks = [];
  2844.       var bookmarkSearchRoot = gRdfContainerUtils.MakeSeq(this._searchDatasource, gRdfService.GetResource("NC:YBBookmarkSearch" + aStoreNumber));
  2845.       var bookmarkResources = bookmarkSearchRoot.GetElements();
  2846.       var hasMoreElements = bookmarkResources.hasMoreElements;
  2847.       var getNext = bookmarkResources.getNext;
  2848.  
  2849.       while ( hasMoreElements() ) {
  2850.           myBookmarks[myBookmarks.length] = getNext();    
  2851.       }
  2852.  
  2853.       aCount.value = myBookmarks.length;
  2854.       return myBookmarks;
  2855.   },
  2856.  
  2857.   _findKeywords : function(source, predicate, keywordLets, conjunction) {
  2858.     var s = this._fileDatasource.GetTarget(source, predicate, true);
  2859.     if (s) {
  2860.       s = s.QueryInterface(CI.nsIRDFLiteral).Value;
  2861.       return this._searchStringForKeywords(s, keywordLets, conjunction);
  2862.     }
  2863.     return false;
  2864.   },
  2865.  
  2866.   _searchBookmarks : function(bookmarks, keyword, 
  2867.     totalSearched, totalMatched, bookmarkSearchRoot) {
  2868.     try {
  2869.       this._isBookmarkSearching = true;
  2870.       var os = Components.classes["@mozilla.org/observer-service;1"].
  2871.                 getService(Components.interfaces.nsIObserverService);
  2872.       var sofarSearched = 1;
  2873.  
  2874.       var conjuction = true;
  2875.       var keywordLets = keyword.split('+');
  2876.       var nKeywordLets = keywordLets.length;
  2877.       if (nKeywordLets == 1) {
  2878.         keywordLets = keyword.split(' ');
  2879.         nKeywordLets = keywordLets.length;
  2880.         //conjuction = false;
  2881.       }
  2882.       var i, j, nMatches;
  2883.  
  2884.       while (bookmarks.hasMoreElements()) {
  2885.         if (sofarSearched++ % SOFAR_SEARCHED == 0) {
  2886.           this._postSearchBookmarksProgress(os, totalSearched,
  2887.             sofarSearched, keyword);
  2888.           break;
  2889.         }
  2890.         var bkResource = bookmarks.getNext();
  2891.         var found =
  2892.           this._findKeywords(bkResource, grscName, keywordLets, conjuction) ||
  2893.           this._findKeywords(bkResource, grscUrl, keywordLets, conjuction) ||
  2894.           this._findKeywords(bkResource, grscDesc, keywordLets, conjuction) ||
  2895.           this._findKeywords(bkResource, grscShortcut, keywordLets, conjuction);
  2896.  
  2897.         if (!found) {
  2898.           var tagsArr = [];
  2899.           var tags = this._fileDatasource.GetTargets(bkResource, grscTag, true);
  2900.           while (tags.hasMoreElements()) {
  2901.             var tagValue = tags.getNext().QueryInterface(CI.nsIRDFLiteral)
  2902.               .Value;
  2903.             var normTag = this._normalizeTag(tagValue);
  2904.             if (conjuction) { // save it for later use
  2905.               tagsArr.push(normTag);
  2906.             } else {
  2907.               found = this._searchStringForKeywords(normTag, keywordLets, false);
  2908.               if (found) {
  2909.                 break;
  2910.               }
  2911.             }
  2912.           }
  2913.           if (conjuction) {
  2914.             nMatches = 0;
  2915.             var nTagsToSearch = nKeywordLets;
  2916.             for (i = 0; i < tagsArr.length; ++i) {
  2917.               for (j = 0; j < nTagsToSearch; ++j) {
  2918.                 if (tagsArr[i].indexOf(keywordLets[j]) != -1) {
  2919.                   ++nMatches;
  2920.                   // push the current tag to the non searchable part of the
  2921.                   // array to prevent repeat hits, keeping it in the array
  2922.                   // ensures that it'll be searched for the next bookmark
  2923.                   var temp = keywordLets[nTagsToSearch - 1];
  2924.                   keywordLets[nTagsToSearch - 1]= keywordLets[j];
  2925.                   keywordLets[j] = temp;
  2926.                   --nTagsToSearch;
  2927.  
  2928.                   break;
  2929.                 }
  2930.               }
  2931.               if (nMatches == nKeywordLets) {
  2932.                 found = true;
  2933.                 break;
  2934.               }
  2935.             }
  2936.           }
  2937.         }
  2938.  
  2939.         if (found && bookmarkSearchRoot.IndexOf(bkResource) == -1) {
  2940.           bookmarkSearchRoot.AppendElement(bkResource);
  2941.           totalMatched++;
  2942.         }
  2943.       }
  2944.  
  2945.       this._isBookmarkSearching = false;
  2946.       if (! this._stopBookmarkSearching) {
  2947.         if (!bookmarks.hasMoreElements()) {
  2948.           this._postSearchBookmarksEnd(os, totalMatched, keyword);
  2949.         } else {
  2950.             this._startSearchBookmarksTimer(bookmarks, keyword,
  2951.               totalSearched, totalMatched, bookmarkSearchRoot);
  2952.         }
  2953.       }
  2954.     } catch (e) {
  2955.       logError("_searchBookmarks", e);
  2956.     }
  2957.   },
  2958.  
  2959.   _startSearchBookmarksTimer : function(bookmarks, keyword, 
  2960.     totalSearched, totalMatched, bookmarkSearchRoot) {
  2961.     var self = this;
  2962.     this._bookmarkSearchTimeout =
  2963.       this._hiddenWindow.setTimeout(
  2964.       function(self, bookmarks, keyword, totalSearched,
  2965.         totalMatched, bookmarkSearchRoot) {
  2966.         self._searchBookmarks(bookmarks, keyword, 
  2967.           totalSearched, totalMatched, bookmarkSearchRoot);
  2968.       }, SEARCH_TIMEOUT, self, bookmarks, keyword, 
  2969.         totalSearched, totalMatched, bookmarkSearchRoot);
  2970.   },
  2971.  
  2972.   _startSearchTagsTimer : function(tags, keyword, 
  2973.     totalSearched, totalMatched, tagSearchRoot) {
  2974.     var self = this;
  2975.     this._tagSearchTimeout = this._hiddenWindow.setTimeout(
  2976.         function(self, tags, keyword, totalSearched,
  2977.           totalMatched, tagSearchRoot) {
  2978.             self._searchTags(tags, keyword, totalSearched,
  2979.               totalMatched, tagSearchRoot);
  2980.         }, SEARCH_TIMEOUT,
  2981.         self, tags, keyword, totalSearched, totalMatched,
  2982.           tagSearchRoot);
  2983.   },
  2984.  
  2985.   _postSearchEnd : function(os, totalMatched, keyword, name) {
  2986.     var subject = {
  2987.       type : name,
  2988.       totalMatched : totalMatched,
  2989.       keyword : keyword
  2990.     };
  2991.     subject.wrappedJSObject = subject;
  2992.     os.notifyObservers(subject, "ybookmarkSearch.end", name);
  2993.   },
  2994.  
  2995.   _postSearchBookmarksEnd : function(os, totalMatched, keyword) {
  2996.     this._postSearchEnd(os, totalMatched, keyword, BOOKMARKS);
  2997.   },
  2998.  
  2999.   _postTagsEnd : function(os, totalMatched, keyword, topic) {
  3000.     this._postSearchEnd(os, totalMatched, keyword, TAGS);
  3001.   },
  3002.  
  3003.   _postSearchBegin : function(os, keyword, name) {
  3004.     var subject = { type : name, keyword : keyword };
  3005.     subject.wrappedJSObject = subject;
  3006.     os.notifyObservers(subject, "ybookmarkSearch.begin", name);
  3007.   },
  3008.  
  3009.   _postSearchBookmarksBegin : function(os, keyword) {
  3010.     this._postSearchBegin(os, keyword, BOOKMARKS);
  3011.   },
  3012.  
  3013.   _postSearchTagsBegin : function(os, keyword) {
  3014.     this._postSearchBegin(os, keyword, TAGS);
  3015.   },
  3016.  
  3017.   _postSearchProgress : function(os, totalSearched,
  3018.     sofarSearched, keyword, name) {
  3019.     var subject = {
  3020.       type : name,
  3021.       total : totalSearched,
  3022.       sofar : sofarSearched,
  3023.       keyword : keyword
  3024.     };
  3025.     subject.wrappedJSObject = subject;
  3026.     os.notifyObservers(subject, "ybookmarkSearch.inProgress", name);
  3027.   },
  3028.  
  3029.   _postSearchBookmarksProgress : function(os, totalSearched,
  3030.     sofarSearched, keyword) {
  3031.     this._postSearchProgress(os, totalSearched, sofarSearched, keyword,
  3032.       BOOKMARKS);
  3033.   },
  3034.  
  3035.   _postSearchTagsProgress : function(os, totalSearched,
  3036.     sofarSearched, keyword) {
  3037.     this._postSearchProgress(os, totalSearched, sofarSearched, keyword,
  3038.       TAGS);
  3039.   },
  3040.   /*
  3041.    * Get the tags suggestion based on the keyword input
  3042.    *
  3043.    * @param aKeyword keyword input
  3044.    *
  3045.    */
  3046.   getTagSuggestions : function(aKeyword, keepExactMatch) {
  3047.     traceIn("getTagSuggestions");
  3048.     var nsArray = new NSArray();
  3049.  
  3050.     try {
  3051.       if (! aKeyword || aKeyword.length == 0) {
  3052.         return nsArray;
  3053.       }
  3054.       
  3055.       if(!keepExactMatch) keepExactMatch = false;
  3056.       
  3057.       var keyword = aKeyword.toLowerCase();
  3058.       var plusIndex = keyword.lastIndexOf('+');
  3059.       if (plusIndex == keyword.length - 1) { // the user has typed nothing useful
  3060.         return nsArray;
  3061.       }
  3062.       if (plusIndex > -1) {
  3063.         // reducing search prefix to the last constituent
  3064.         keyword = keyword.substring(plusIndex + 1, keyword.length);
  3065.       }
  3066.  
  3067.       var jsArray = [];
  3068.       var tags = this._getTagRoot().GetElements();
  3069.       var found = false;
  3070.       while (tags.hasMoreElements()) {
  3071.         found = false;
  3072.         var tagResource = tags.getNext();
  3073.         var tag = this._getTargetValue(tagResource, grscTagValue);
  3074.         if (tag) {
  3075.           tag = tag.toLowerCase();
  3076.           if (tag.indexOf(keyword) == 0 && (tag.length != keyword.length || keepExactMatch)) {
  3077.              childCount = this._getTargetValue(tagResource, grscChildCount);
  3078.              if (childCount > 0) {
  3079.                jsArray.push({ tag : tag , count : childCount });
  3080.              }
  3081.           }
  3082.         }
  3083.       }
  3084.  
  3085.       // sort the array
  3086.       jsArray.sort(function(a, b) {
  3087.         var x = a.count;
  3088.         var y = b.count;
  3089.  
  3090.         var w = a.tag;
  3091.         var v = b.tag;
  3092.  
  3093.         return ((x == y) ? ((w < v) ? -1 : ((w > v) ? 1 : 0)) :
  3094.           ((x > y) ? -1 : ((x < y) ? 1 : 0)));
  3095.       });
  3096.  
  3097.       for (var i = 0; i < jsArray.length; i++) {
  3098.         var propertyBag = new HashPropertyBag();
  3099.         propertyBag.setProperty("tag", jsArray[i].tag);
  3100.         propertyBag.setProperty("count", jsArray[i].count);
  3101.         nsArray.appendElement(propertyBag, false);
  3102.       }
  3103.     } catch (e) {
  3104.       yDebug.print("getTagSuggestions"+e, YB_LOG_MESSAGE);
  3105.     } finally {
  3106.       traceOut("getTagSuggestions");
  3107.     }
  3108.  
  3109.     return nsArray;
  3110.   },
  3111.  
  3112.   /*
  3113.    * Remove all tags for a given URL. This is useful when bookmark is
  3114.    * synched with the service.
  3115.    * Most services do not provide the data like since the last update
  3116.    * what tags were added and what tags were deleted. Using the new tags
  3117.    * list and removing the tags not in the list.  Then, adding the new ones in.
  3118.    *
  3119.    * @param bookmarkResource The bookmark for which tags to be removed.
  3120.    *
  3121.    */
  3122.   _removeAllTagsForResource : function (bookmarkResource) {
  3123.     var tags = this._fileDatasource.GetTargets(bookmarkResource, grscTag, true);
  3124.     while (tags.hasMoreElements()) {
  3125.       var tag = tags.getNext().QueryInterface(CI.nsIRDFLiteral);
  3126.       this._fileDatasource.Unassert(bookmarkResource, grscTag, tag, true);
  3127.       this._removeBookmarkFromTagContainer(tag.Value, bookmarkResource);
  3128.     }
  3129.     var favTags = this._favoriteTagsRoot.GetElements();
  3130.     while (favTags.hasMoreElements()) {
  3131.       var favTag = favTags.getNext();
  3132.       favTag.QueryInterface(Components.interfaces.nsIRDFResource);
  3133.       this._removeMatchingContent(favTag, bookmarkResource);
  3134.     }
  3135.   },
  3136.  
  3137.   _getResourceByPredicate : function (aName, predicate,
  3138.     insertIfAbsent, root) {
  3139.     var target = gRdfService.GetLiteral(aName);
  3140.     var resource = this._fileDatasource.GetSource(predicate, target, true);
  3141.  
  3142.     if (! resource) {
  3143.       if (insertIfAbsent) {
  3144.         resource = gRdfService.GetAnonymousResource();
  3145.         this._fileDatasource.Assert(resource, predicate, target, true);
  3146.         root.AppendElement(resource);
  3147.       }
  3148.     }
  3149.     return resource;
  3150.   },
  3151.  
  3152.   _getTagResource : function(aTag, insertIfAbsent) {
  3153.     /*
  3154.      * tags in the tagRoot are stored in lower case. But in bookmarks
  3155.      * it is stored as we got from the service provider.
  3156.      */
  3157.     var norTag = this._normalizeTag(aTag);
  3158.     return this._getResourceByPredicate(norTag, grscTagValue, insertIfAbsent,
  3159.       this._getTagRoot());
  3160.   },
  3161.  
  3162.   _getBookmarkResource : function(aUrl, insertIfAbsent) {
  3163.     return this._getResourceByPredicate(aUrl, grscUrl,
  3164.       insertIfAbsent, this._getBookmarkRoot());
  3165.   },
  3166.  
  3167.   _getFavoriteTagResouce : function(aTag, insertIfAbsent) {
  3168.     var norTag = this._normalizeTag(aTag);
  3169.     return this._getResourceByPredicate(norTag, grscFavoriteTagValue,
  3170.       insertIfAbsent, this._favoriteTagsRoot);
  3171.   },
  3172.  
  3173.   _getBundleResource : function(aName, insertIfAbsent) {
  3174.     var norName = this._normalizeTag(aName);
  3175.     return this._getResourceByPredicate(norName, grscBundleValue,
  3176.       insertIfAbsent, this._bundleRoot);
  3177.   },
  3178.  
  3179.  
  3180.   /*********************************************************************
  3181.    *                           Transaction Functions                   *
  3182.    *********************************************************************/
  3183.  
  3184.   /**
  3185.    *  Add a  transaction (e.g. add, edit and delete bookmark) to the _datasource
  3186.    *
  3187.    *  @param aType the type of the transaction
  3188.    *  @param aState the state of the transaction   i.e. 0 - uninitialized,
  3189.    *  1 = sent, 2 = completed
  3190.    *  @param aJSON the object which contains the bookmark's information
  3191.    *
  3192.    */
  3193.   addTransaction : function (aType, aState, aJSON) {
  3194.     traceIn("addTransaction");
  3195.     try {
  3196.       if (aJSON.wrappedJSObject) {
  3197.        aJSON = aJSON.wrappedJSObject;
  3198.       }
  3199.  
  3200.       if (aJSON["localOnly"] && aJSON["localOnly"] == "true") {
  3201.         switch(aType) {
  3202.           case "addBookmark":
  3203.             return;
  3204.           break;
  3205.           case "editBookmark":
  3206.             // change the type to a delete operation to ensure this is not on
  3207.             // remote
  3208.             aType = "deleteBookmark";
  3209.           break;
  3210.         }
  3211.       }
  3212.  
  3213.       var transactionResource = gRdfService.GetAnonymousResource();
  3214.       for (var name in aJSON) {
  3215.         if (name == "wrappedJSObject") {
  3216.           continue;
  3217.         }
  3218.  
  3219.         var val = "";
  3220.         if (name == "tags") {
  3221.           if (aJSON[name].enumerate) {
  3222.             var iter = aJSON[name].enumerate();
  3223.             while (iter.hasMoreElements()) {
  3224.               var str = iter.getNext();
  3225.               str.QueryInterface(Components.interfaces.nsISupportsString);
  3226.               if (val.length > 0) {
  3227.                 val += " ";
  3228.               }
  3229.               val += str.data;
  3230.             }
  3231.           } else {
  3232.             val = aJSON[name].join(' ');
  3233.           }
  3234.         } else {
  3235.           val = aJSON[name];
  3236.         }
  3237.         if (val) {
  3238.           this._fileDatasource.Assert(transactionResource,
  3239.             gRdfService.GetResource(NS_TRANSACTION_BASE + name),
  3240.             gRdfService.GetLiteral(val), true);
  3241.         }
  3242.       }
  3243.  
  3244.       this._fileDatasource.Assert(transactionResource,
  3245.         gRdfService.GetResource(NS_TRANSACTION_BASE + "transactionType"),
  3246.         gRdfService.GetLiteral(aType), true);
  3247.  
  3248.       this._fileDatasource.Assert(transactionResource,
  3249.         gRdfService.GetResource(NS_TRANSACTION_BASE + "transactionState"),
  3250.         gRdfService.GetIntLiteral(aState), true);
  3251.  
  3252.       var time = parseInt(((new Date()).getTime())/1000) + "";
  3253.       this._fileDatasource.Assert(transactionResource,
  3254.         gRdfService.GetResource(NS_TRANSACTION_BASE + "transactionTime"),
  3255.         gRdfService.GetLiteral(time), true);
  3256.       this._transactionRoot.AppendElement(transactionResource);
  3257.  
  3258.       this._scheduleFlush();
  3259.     } catch (e) {
  3260.       logError("addTransaction", e);
  3261.     } finally {
  3262.       traceOut("addTransaction");
  3263.     }
  3264.   },
  3265.  
  3266.   /**
  3267.    *  Remove a transaction from the datasource
  3268.    *
  3269.    *  @param aType the type of the transaction
  3270.    *  @param aUrl the bookmark url
  3271.    *
  3272.    */
  3273.   removeTransaction : function (aType, aUrl) {
  3274.     traceIn("removeTransaction");
  3275.     try {
  3276.       var urlRes = gRdfService.GetResource(NS_TRANSACTION_BASE + "url");
  3277.       var typeRes = gRdfService.GetResource(NS_TRANSACTION_BASE +
  3278.         "transactionType");
  3279.       var stateRes = gRdfService.GetResource(NS_TRANSACTION_BASE +
  3280.         "transactionState");
  3281.       var txnResources = this._fileDatasource.GetSources(urlRes,
  3282.         gRdfService.GetLiteral(aUrl), true);
  3283.  
  3284.       while (txnResources.hasMoreElements()) {
  3285.         var txnResource = txnResources.getNext();
  3286.         var txnType = this._getTargetValue(txnResource, typeRes);
  3287.         if (txnType == aType) {
  3288.           this._deleteAllChildren(txnResource);
  3289.           this._transactionRoot.RemoveElement(txnResource, false);
  3290.           this._scheduleFlush();
  3291.           break;
  3292.         }
  3293.       }
  3294.     } catch (e) {
  3295.       logError("removeTransaction", e);
  3296.     } finally {
  3297.       traceOut("removeTransaction");
  3298.     }
  3299.   },
  3300.   /**
  3301.    *  Remove a transaction from the _datasource
  3302.    *
  3303.    *  @param aType the type of the transaction
  3304.    *  @param aUrl the bookmark url
  3305.    *
  3306.    */
  3307.   _removeTransactions : function (aType, aUrl) {
  3308.     var urlRes = gRdfService.GetResource(NS_TRANSACTION_BASE + "url");
  3309.     var typeRes = gRdfService.GetResource(NS_TRANSACTION_BASE +
  3310.       "transactionType");
  3311.     var txnResources = this._fileDatasource.GetSources(urlRes,
  3312.       gRdfService.GetLiteral(aUrl), true);
  3313.  
  3314.     while (txnResources.hasMoreElements()) {
  3315.       var txnResource = txnResources.getNext();
  3316.  
  3317.       var txnType = this._getTargetValue(txnResource, typeRes);
  3318.       var txnState = this._getTargetValue(txnResource, grscState);
  3319.  
  3320.       if (txnType == aType) {
  3321.         this._deleteAllChildren(txnResource);
  3322.         this._transactionRoot.RemoveElement(txnResource, false);
  3323.         this._scheduleFlush();
  3324.         break;
  3325.       }
  3326.     }
  3327.   },
  3328.  
  3329.   /**
  3330.    *  Remove all transactions in particular state.
  3331.    *
  3332.    *  @param aState the state of the transactions.
  3333.    *  i.e. 0 - uninitialized, 1= sent, 2 = completed, 10 = all
  3334.    */
  3335.   removeAllTransactions : function (aState) {
  3336.     traceIn("removeAllTransactions");
  3337.     try {
  3338.       var needFlush = false;
  3339.       var txnResources = this._transactionRoot.GetElements();
  3340.       while (txnResources.hasMoreElements()) {
  3341.         var txnResource = txnResources.getNext();
  3342.         var txnState = this._getTargetValue(txnResource, grscState);
  3343.         if (aState == 10 || txnState == aState) {
  3344.           this._deleteAllChildren(txnResource);
  3345.           this._transactionRoot.RemoveElement(txnResource, false);
  3346.           needFlush = true;
  3347.         }
  3348.       }
  3349.  
  3350.       if (needFlush) {
  3351.         this._scheduleFlush();
  3352.       }
  3353.     } catch (e) {
  3354.       logError("removeAllTransactions", e);
  3355.     } finally {
  3356.       traceOut("removeAllTransactions");
  3357.     }
  3358.   },
  3359.  
  3360.   /**
  3361.    *  Set the state of a transaction
  3362.    *
  3363.    *  @param aType the type of the transaction
  3364.    *  @param aUrl the bookmark url
  3365.    *  @param aState the state of the transactions. i.e.
  3366.    *  0 - uninitialized, 1 - sent, 2 - completed, 3 - failed
  3367.    */
  3368.   setTransactionState : function (aType, aUrl, aState) {
  3369.     traceIn("setTransactionState");
  3370.     try {
  3371.       var urlRes = gRdfService.GetResource(NS_TRANSACTION_BASE + "url");
  3372.       var typeRes = gRdfService.GetResource(NS_TRANSACTION_BASE +
  3373.         "transactionType");
  3374.  
  3375.       var txnResources = this._fileDatasource.
  3376.         GetSources(gRdfService.GetResource(NS_TRANSACTION_BASE + "url"),
  3377.         gRdfService.GetLiteral(aUrl), true);
  3378.       var newState = gRdfService.GetIntLiteral(aState);
  3379.       var time = parseInt(((new Date()).getTime())/1000) + "";
  3380.       var newTime = gRdfService.GetLiteral(time);
  3381.  
  3382.       while (txnResources.hasMoreElements()) {
  3383.         var txnResource = txnResources.getNext();
  3384.  
  3385.         url = this._getTargetValue(txnResource, urlRes);
  3386.         var txnType = this._getTargetValue(txnResource, typeRes);
  3387.         var txnState = this._getTargetValue(txnResource, grscState, true);
  3388.  
  3389.  
  3390.         if (url == aUrl && txnType == aType && txnState != aState) {
  3391.           this._fileDatasource.Change(txnResource, grscState,
  3392.             gRdfService.GetIntLiteral(txnState), newState, true);
  3393.           if (aState == 2) {
  3394.             this._removeTransactions(aType, aUrl);
  3395.           } else {
  3396.             var txnTime = this._fileDatasource.GetTarget(txnResource, grscTime, true);
  3397.             if (txnTime) {
  3398.               this._fileDatasource.Change(txnResource, grscTime, txnTime,
  3399.                 newTime, true);
  3400.             } else {
  3401.               this._fileDatasource.Assert(txnResource, grscTime, newTime, true);
  3402.             }
  3403.           }
  3404.           this._scheduleFlush();
  3405.           break;
  3406.         }
  3407.       }
  3408.     } catch (e) {
  3409.       logError("setTransactionState", e);
  3410.     } finally {
  3411.       traceOut("setTransactionState");
  3412.     }
  3413.   },
  3414.  
  3415.   /**
  3416.    *  Set the state of all transactions
  3417.    *
  3418.    *  @param aState the state of the transaction.
  3419.    *  i.e. 0 - uninitialized, 1 - sent, 2 - completed. 3 - failed
  3420.    */
  3421.   _setAllTransactionsState : function (aState) {
  3422.     var transactionResources = this._transactionRoot.GetElements();
  3423.     var time = parseInt(((new Date()).getTime()) / 1000) + "";
  3424.     var newTime = gRdfService.GetLiteral(time);
  3425.     var newState = gRdfService.GetIntLiteral(aState);
  3426.     var toFlush = false;
  3427.  
  3428.     while (transactionResources.hasMoreElements()) {
  3429.       var transactionResource = transactionResources.getNext();
  3430.       var transactionState = this._getTargetValue(transactionResource,
  3431.         grscState);
  3432.       if (transactionState != aState) {
  3433.         this._fileDatasource.Change(transactionResource, grscState,
  3434.         gRdfService.GetIntLiteral(transactionState), newState, true);
  3435.  
  3436.         var transactionTime = this._fileDatasource.GetTarget(transactionResource,
  3437.           grscTime, true);
  3438.         if (transactionTime) {
  3439.           this._fileDatasource.Change(transactionResource, grscTime,
  3440.             transactionTime, newTime, true);
  3441.         } else {
  3442.           this._fileDatasource.Assert(transactionResource, grscTime, newTime, true);
  3443.         }
  3444.         toFlush = true;
  3445.       }
  3446.     }
  3447.     if (toFlush) {
  3448.       this._scheduleFlush();
  3449.     }
  3450.   },
  3451.  
  3452.   /**
  3453.    *  Get all transactions from the _datasource
  3454.    *
  3455.    *  @return the MutableArray contains all transactions in hashpropertybag
  3456.    *  format
  3457.    *
  3458.    */
  3459.   getTransactions : function () {
  3460.     traceIn("getTransactions");
  3461.     var nsArray = new NSArray();
  3462.     try {
  3463.       var txnResources = this._transactionRoot.GetElements();
  3464.       while (txnResources.hasMoreElements()) {
  3465.         var txnResource = txnResources.getNext();
  3466.  
  3467.         var propertyBag = new HashPropertyBag();
  3468.         var url = null;
  3469.  
  3470.         var txnProps = this._fileDatasource.ArcLabelsOut(txnResource);
  3471.         while (txnProps.hasMoreElements()) {
  3472.           var predicate = txnProps.getNext();
  3473.           var name = this._getResourcePropertyValue(predicate);
  3474.           name  = name.split("#")[1];
  3475.           var val = this._getTargetValue(txnResource, predicate);
  3476.           propertyBag.setProperty(name, val);
  3477.           if (name.match(/^url/)) {
  3478.             url = val;
  3479.           }
  3480.  
  3481.           yDebug.print("name:" + name + "   , val:" + val);
  3482.         }
  3483.  
  3484.         // check if the bookmark is having microsummary. If so,
  3485.         // set microsummary property to the URL for generator URI
  3486.         if (url && Components.classes[kMicrosummaryContractID]) {
  3487.           var msService = Components.classes[kMicrosummaryContractID].
  3488.             getService(kIMicrosummaryService);
  3489.           var bookmarkResource = this._getBookmarkResource(url, false);
  3490.           if (bookmarkResource) {
  3491.             var microsummary = msService.getMicrosummary(bookmarkResource);
  3492.             if (microsummary) {
  3493.               propertyBag.setProperty("microsummary",
  3494.                 microsummary.generator.uri.spec);
  3495.             } else {
  3496.               yDebug.print("MICROSUMMARY NOT SET");
  3497.             }
  3498.           } else {
  3499.             yDebug.print("BOOKMARK IS NOT PRESENT");
  3500.           }
  3501.         } else {
  3502.           yDebug.print("EITHER URL IS EMPTY OR MICROSUMMARY NOT ENABLED",
  3503.             YB_LOG_MESSAGE);
  3504.         }
  3505.         nsArray.appendElement(propertyBag, false);
  3506.       }
  3507.     } catch (e) {
  3508.       logError("getTransactions", e);
  3509.     } finally {
  3510.       traceOut("getTransactions");
  3511.     }
  3512.     return nsArray;
  3513.   },
  3514.  
  3515.   /**
  3516.    *  Get the number of transactions in the _datasource
  3517.    *
  3518.    *  @param aType the type of the transaction. i.e. addBookmark,
  3519.    *    editBookmark, deleteBookmark, all/""
  3520.    *  @param aState the state of the transactions.
  3521.    *  i.e. 0 - uninitialized, 1 - sent, 2 - completed, 3 - failed, 10 - all
  3522.    *
  3523.    *  @return number the number of transactions
  3524.    *
  3525.    */
  3526.   getNumberOfTransactions : function(aType, aState) {
  3527.     traceIn("getNumberOfTransactions");
  3528.     try {
  3529.       var urlRes = gRdfService.GetResource(NS_TRANSACTION_BASE + "url");
  3530.       var typeRes = gRdfService.GetResource(NS_TRANSACTION_BASE
  3531.         + "transactionType");
  3532.  
  3533.       var result = 0;
  3534.  
  3535.       var txnResources = this._transactionRoot.GetElements();
  3536.       while (txnResources.hasMoreElements()) {
  3537.         var txnResource = txnResources.getNext();
  3538.         txnType = this._getTargetValue(txnResource, typeRes);
  3539.         txnState = this._getTargetValue(txnResource, grscState);
  3540.  
  3541.         if (aType == "all" || aType.length == 0 || txnType == aType) {
  3542.           if (txnState == aState || aState == 10) {
  3543.             result ++;
  3544.           }
  3545.         }
  3546.       }
  3547.       return result;
  3548.     } catch (e) {
  3549.       logError("getNumberOfTransactions", e);
  3550.     } finally {
  3551.       traceOut("getNumberOfTransactions");
  3552.     }
  3553.     return 0;
  3554.   },
  3555.  
  3556.   /**
  3557.    *  Reset the 'sent' and 'failed' transactions to 'uninitialized'
  3558.    *  after a period of time
  3559.    *
  3560.    */
  3561.   restateTransactions : function () {
  3562.     traceIn("restateTransactions");
  3563.     try {
  3564.       var urlRes = gRdfService.GetResource(NS_TRANSACTION_BASE + "url");
  3565.       var typeRes = gRdfService.GetResource(NS_TRANSACTION_BASE +
  3566.         "transactionType");
  3567.  
  3568.       var time = (new Date).getTime();
  3569.       const timeDiff = 60 * 1000;
  3570.  
  3571.       var txnResources = this._transactionRoot.GetElements();
  3572.       while (txnResources.hasMoreElements()) {
  3573.         var txnResource = txnResources.getNext();
  3574.         var url = this._getTargetValue(txnResource, urlRes);
  3575.         var txnState = this._getTargetValue(txnResource, grscState);
  3576.         var txnTime = this._getTargetValue(txnResource, grscTime);
  3577.         txnTime = parseInt(txnTime);
  3578.         if ((isNaN(txnTime) || ((txnTime * 1000) + timeDiff) < time) &&
  3579.           (txnState == 1 || txnState == 3)) {
  3580.           var txnType = this._getTargetValue(txnResource, typeRes);
  3581.           this.setTransactionState(txnType, url, 0);
  3582.           yDebug.print("Restate transaction to 0: " + url);
  3583.         } else {
  3584.           yDebug.print("Transaction too new to restate: " + url);
  3585.         }
  3586.       }
  3587.     } catch (e) {
  3588.       logError("restateTransactions", e);
  3589.     } finally {
  3590.       traceOut("restateTransactions");
  3591.     }
  3592.   },
  3593.  
  3594.   /**
  3595.    *  Reset all transactions at the browser startup.
  3596.    *  Operations include removing all completed transactions and
  3597.    *  reset other transactions' state to uninitialized
  3598.    */
  3599.   _resetTransactions : function() {
  3600.     this.removeAllTransactions(2);
  3601.     this._setAllTransactionsState(0);
  3602.   },
  3603.  
  3604.   /**
  3605.    *  Get the resource's property value
  3606.    *
  3607.    *  @param node the resource's property
  3608.    *
  3609.    *  @return the resource's property value
  3610.    */
  3611.   _getResourcePropertyValue : function(node) {
  3612.     if (node) {
  3613.       try {
  3614.         node = node.QueryInterface(Components.interfaces.nsIRDFLiteral);
  3615.         return node.Value;
  3616.       } catch (e) {
  3617.         try {
  3618.           node = node.QueryInterface(Components.interfaces.nsIRDFInt);
  3619.           return node.Value;
  3620.         } catch (e) {
  3621.           node = node.QueryInterface(Components.interfaces.nsIRDFResource);
  3622.           return node.Value;
  3623.         }
  3624.       }
  3625.     }
  3626.     return "";
  3627.   },
  3628.  
  3629.   _getTargetValue : function(source, predicate) {
  3630.     return this._getResourcePropertyValue(
  3631.       this._fileDatasource.GetTarget(source, predicate, true));
  3632.     return "";
  3633.   },
  3634.   /**
  3635.    *  Get all bookmarks' metahash and urlhash from the _datasource
  3636.    *
  3637.    *  @return the MutableArray contains all transactions
  3638.    *  and each of them is in hashPropertyBag format
  3639.    *
  3640.    */
  3641.   getBookmarkHashes : function () {
  3642.     traceIn("getBookmarkHashes");
  3643.     var nsArray = new NSArray();
  3644.     try {
  3645.       var bookmarks = this._getBookmarkRoot().GetElements();
  3646.       counter = 0;
  3647.       while (bookmarks.hasMoreElements()) {
  3648.         counter++;
  3649.         var bookmarkResource = bookmarks.getNext();
  3650.         var url = this._getTargetValue(bookmarkResource, grscUrl, true);
  3651.         var localOnly = this._getTargetValue(bookmarkResource, grscLocalOnly);
  3652.         if (localOnly == "true") {
  3653.           yDebug.print("====>getBookmarkHashes (Local only) <== " + url,
  3654.             YB_LOG_MESSAGE);
  3655.         } else {
  3656.           var hash = this._getTargetValue(bookmarkResource, grscHash);
  3657.           if (hash) {
  3658.             var metahash = this._getTargetValue(bookmarkResource, grscMetaHash);
  3659.             var data = new HashPropertyBag();
  3660.             data.setProperty("hash", hash);
  3661.             data.setProperty("metahash", metahash);
  3662.             nsArray.appendElement(data, false);
  3663.             if (!metahash) {
  3664.               yDebug.print("====>getBookmarkHashes metahash is missing <== "
  3665.                 + url, YB_LOG_MESSAGE);
  3666.             }
  3667.           } else {
  3668.             yDebug.print("====>getBookmarkHashes hash is missing <== " + url,
  3669.               YB_LOG_MESSAGE);
  3670.           }
  3671.         }
  3672.       }
  3673.  
  3674.       yDebug.print("Local hashes counter :" + counter, YB_LOG_MESSAGE);
  3675.     } catch (e) {
  3676.       logError("getBookmarkHashes", e);
  3677.     } finally {
  3678.       traceOut("getBookmarkHashes");
  3679.     }
  3680.     return nsArray;
  3681.   },
  3682.  
  3683.   /**
  3684.    * Delete the bookmark and all its associated tags from the database.
  3685.    * If same tag is used by multiple bookmarks, only the association
  3686.    * between bookmark and the tag is removed. Tag is retained in the system.
  3687.    *
  3688.    * @param aHash boookmark which contain this url hash would be removed from
  3689.    * the system.
  3690.    *
  3691.    * @return false if url hash is not present in the system, true otherwise.
  3692.    *
  3693.    */
  3694.   deleteBookmarkForHash : function (aHash) {
  3695.     traceIn("deleteBookmarkForHash");
  3696.     try {
  3697.       var hashResource = gRdfService.GetLiteral(aHash);
  3698.       var bookmarkResource = this._fileDatasource.GetSource(grscHash, hashResource,
  3699.         true);
  3700.       var url = this._getTargetValue(bookmarkResource, grscUrl);
  3701.       if (url) {
  3702.         yDebug.print("Delete url ===> " + url);
  3703.         return this.deleteBookmark(url);
  3704.       } else {
  3705.         return false;
  3706.       }
  3707.     } catch (e) {
  3708.       logError("deleteBookmarkForHash", e);
  3709.     } finally {
  3710.       traceOut("deleteBookmarkForHash");
  3711.     }
  3712.     return false;
  3713.   },
  3714.  
  3715.   getBundles : function(outCount) {
  3716.     traceIn("getBundles");
  3717.     var result = [];
  3718.     try {
  3719.       var bundles = this._bundleRoot.GetElements();
  3720.       while (bundles.hasMoreElements()) {
  3721.         var bnRsrc = bundles.getNext().QueryInterface(CI.nsIRDFResource);
  3722.         var bnJson = this._genBundleJSON(bnRsrc);
  3723.         if (bnJson) {
  3724.           result.push(bnJson);
  3725.         }
  3726.       }
  3727.     } catch (e) {
  3728.       logError("getBundles", e);
  3729.     } finally {
  3730.       traceOut("getBundles");
  3731.     }
  3732.     if (outCount) {
  3733.       outCount.value = result.length;
  3734.     }
  3735.     return result;
  3736.   },
  3737.   
  3738.   getBundle : function(aBundle) {
  3739.     traceIn("getBundle");
  3740.     try {
  3741.       var bnResource = this._getBundleResource(aBundle, false);
  3742.       if (bnResource) {
  3743.         return this._genBundleJSON(bnResource);
  3744.       } 
  3745.     } catch (e) {
  3746.       logError("getBundle", e);
  3747.     }
  3748.     return null;
  3749.   },
  3750.   
  3751.   setBundles : function(aBundles) {
  3752.     traceIn("setBundles");
  3753.     try {
  3754.       aBundles.QueryInterface(Components.interfaces.nsIArray);
  3755.       for (var e = aBundles.enumerate(); e.hasMoreElements(); ) {
  3756.         var bag = e.getNext().QueryInterface(CI.nsIPropertyBag);
  3757.         var name = bag.getProperty("name").
  3758.           QueryInterface(CI.nsISupportsString).data;
  3759.         var tags = bag.getProperty("tags").
  3760.           QueryInterface(CI.nsISupportsString).data;
  3761.         tags = ybookmarksUtils.jsArrayToNs(tags.split(" "));
  3762.         var bundle = {
  3763.           name : name,
  3764.           tags : tags
  3765.         };
  3766.         this.setBundle(bundle);
  3767.       }
  3768.     } catch (e) {
  3769.       logError("setBundles", e);
  3770.     } finally {
  3771.       traceOut("setBundles");
  3772.     }
  3773.   },
  3774.   
  3775.   setBundle : function(aBundle) {
  3776.     traceIn("setBundle");
  3777.     try {
  3778.       var bnResource = this._getBundleResource(aBundle.name, true);
  3779.       var bnContainer = gRdfContainerUtils.MakeSeq(this._fileDatasource,
  3780.         bnResource);    
  3781.       emptyContainer(bnContainer);
  3782.  
  3783.       for(var i = 0; i < aBundle.tags.length; i++) {
  3784.         var nsTag = aBundle.tags.queryElementAt(i,
  3785.           Components.interfaces.nsISupportsString);
  3786.         var tagResource = this._getTagResource(nsTag.data, true);
  3787.         bnContainer.AppendElement(tagResource);
  3788.       }
  3789.  
  3790.       if (aBundle.order) {
  3791.         this._setStringProperty(bnResource, grscBundleOrder, aBundle.order);
  3792.       } else if (! this._fileDatasource.GetTarget(bnResource,
  3793.         grscBundleOrder, true)) {
  3794.         this._setStringProperty(bnResource, grscBundleOrder,
  3795.           FAVTAG_ORDER_DEFAULT);
  3796.       }
  3797.       this._scheduleFlush();
  3798.     } catch (e) {
  3799.       logError("setBundle", e);
  3800.     } finally {
  3801.       traceOut("setBundle");
  3802.     }
  3803.   },
  3804.   
  3805.   clearBundles : function () {
  3806.     var bundles = this._bundleRoot.GetElements();
  3807.     while (bundles.hasMoreElements()) {
  3808.       var bnRsrc = bundles.getNext().QueryInterface(CI.nsIRDFResource);
  3809.       this._removeBundle(bnRsrc);
  3810.       this._bundleRoot.RemoveElement(bnRsrc, false);
  3811.       this._scheduleFlush();
  3812.     }
  3813.   },
  3814.   
  3815.   deleteBundle : function (aBundle) {
  3816.     var bnResource = this._getBundleResource(aBundle, false);
  3817.     this._deleteAllChildren(bnResource);
  3818.     this._removeFromBundlesRoot(bnResource);
  3819.     this._scheduleFlush();
  3820.   },
  3821.  
  3822.   moveBundle : function (aBundle, aIndex) {
  3823.     var bnResource = this._getBundleResource(aBundle, false);
  3824.  
  3825.     if (bnResource && this._bundleRoot.IndexOf(bnResource) != -1) {
  3826.       this._bundleRoot.RemoveElement(bnResource, true);
  3827.       this._bundleRoot.InsertElementAt(bnResource, aIndex, true);
  3828.       this._scheduleFlush();
  3829.     }
  3830.   },
  3831.  
  3832.   _removeBundle : function(bundleResource) {
  3833.     var tags = gRdfContainerUtils.MakeSeq(this._fileDatasource, bundleResource);
  3834.     var tagEnum = tags.GetElements();
  3835.     while (tagEnum.hasMoreElements()) {
  3836.       var tagResource = tagEnum.getNext();
  3837.       if (this._getTargetInt(tagResource, grscChildCount) == 0) {
  3838.         this._deleteAllChildren(tagResource);
  3839.         this._removeFromTagRoot(tagResource);
  3840.       }
  3841.     }
  3842.     this._deleteAllChildren(bundleResource);
  3843.   },
  3844.  
  3845.   _genBundleJSON : function(aBundleResource) {
  3846.     var name = this._fileDatasource.GetTarget(aBundleResource,
  3847.       grscBundleValue, true);
  3848.     if (name) {
  3849.       name = name.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  3850.     } else {
  3851.       yDebug.print("Name resource not found for " + aBundleResource.Value);
  3852.       name = "";
  3853.     }
  3854.     var tagContainer = gRdfContainerUtils.MakeSeq(this._fileDatasource,
  3855.       aBundleResource);
  3856.     var tagEnum = tagContainer.GetElements();
  3857.     var nsTags = new NSArray();
  3858.     while (tagEnum.hasMoreElements()) {
  3859.       var tagRsrc = tagEnum.getNext().QueryInterface(CI.nsIRDFResource);
  3860.       var tagLiteral = this._fileDatasource.GetTarget(tagRsrc, grscTagValue, true);
  3861.       if (tagLiteral) {
  3862.         tagLiteral.QueryInterface(CI.nsIRDFLiteral);
  3863.         var nsTag = new NSString();
  3864.         nsTag.data = tagLiteral.Value;
  3865.         nsTags.appendElement(nsTag, false);
  3866.       } else {
  3867.         yDebug.print("tag " + tagRsrc.Value + " has no name");
  3868.       }
  3869.     }
  3870.  
  3871.     var orderLiteral = this._fileDatasource.GetTarget(aBundleResource,
  3872.       grscBundleOrder, true);
  3873.     var order = orderLiteral ?
  3874.       orderLiteral.QueryInterface(CI.nsIRDFLiteral).Value :
  3875.       FAVTAG_ORDER_DEFAULT;
  3876.  
  3877.     var retVal =  {
  3878.       name : name,
  3879.       tags : nsTags,
  3880.       order : order
  3881.     };
  3882.     retVal.wrappedJSObject = retVal;
  3883.  
  3884.     return retVal;
  3885.   },
  3886.   
  3887.   /**
  3888.    * Creates a new triplet <subject, predicate, newValue> if there
  3889.    * is no triplet having subject and predicate. If there does
  3890.    * exist a triplet for subject and predicate, replaces the object
  3891.    * with the new value.
  3892.    */
  3893.   _setProperty : function(subject, predicate, newValue) {
  3894.     var currentValue = this._fileDatasource.GetTarget(
  3895.       subject, predicate, true);
  3896.     if (currentValue) {
  3897.       this._fileDatasource.Change(subject, predicate, currentValue,
  3898.         newValue, true);
  3899.     } else {
  3900.       this._fileDatasource.Assert(subject, predicate, newValue, true);
  3901.     }
  3902.   },
  3903.  
  3904.   _setStringProperty : function(subject, predicate, value) {
  3905.     this._setProperty(subject, predicate, gRdfService.GetLiteral(value));
  3906.   },
  3907.  
  3908.   _setBooleanProperty : function(subject, predicate, value) {
  3909.     this._setProperty(subject, predicate, gRdfService.GetLiteral(value));
  3910.   },
  3911.  
  3912.   _setDateProperty : function(subject, predicate, value) {
  3913.     this._setProperty(subject, predicate, gRdfService.GetDateLiteral(value));
  3914.   },
  3915.  
  3916.   _setSharedFlag : function(subject, aShared) {
  3917.     this._setBooleanProperty(subject, grscShared, aShared);
  3918.   },
  3919.  
  3920.   _setLocalOnlyFlag : function(subject, localOnly) {
  3921.     this._setBooleanProperty(subject, grscLocalOnly, localOnly);
  3922.   },
  3923.  
  3924.   QueryInterface : function(aIID) {
  3925.     if (!aIID.equals(nsIYBookmarksStoreService) &&
  3926.        !aIID.equals(nsISupports)) {
  3927.          throw Components.results.NS_ERROR_NO_INTERFACE;
  3928.     }
  3929.  
  3930.     return this;
  3931.   }
  3932. };
  3933.  
  3934. /*
  3935.  * Class factory
  3936.  */
  3937. function factory(filename) {
  3938.   return {
  3939.     _filename : filename,
  3940.     _singletonObj : null,
  3941.  
  3942.     createInstance : function(aOuter, aIID) {
  3943.       yDebug.print("createInstance called in nsIFactory object");
  3944.       if (aOuter != null) {
  3945.         throw Components.results.NS_ERROR_NO_AGGREGATION;
  3946.       }
  3947.       if (!this._singletonObj) {
  3948.         this._singletonObj = new YBookmarksStoreService(this._filename);
  3949.       }
  3950.       return this._singletonObj.QueryInterface(aIID);
  3951.     }
  3952.   };
  3953. }
  3954.  
  3955. var YBookmarksStoreServiceFactory = factory("delicious.rdf");
  3956. var YBookmarksStoreServiceTestFactory = factory("delicious-test.rdf");
  3957.  
  3958. /*
  3959.  * Module definition
  3960.  */
  3961.  
  3962. var YBookmarksStoreServiceModule = {
  3963.  
  3964.   registerSelf : function(aCompMgr, aFileSpec, aLocation, aType) {
  3965.     yDebug.print("Registering YBookmarksStoreServiceModule", YB_LOG_MESSAGE);
  3966.     yDebug.print("registerSelf: aFileSpec => " + aFileSpec);
  3967.     yDebug.print("registerSelf: aLocation => " + aLocation);
  3968.     yDebug.print("registerSelf: aType => " + aType);
  3969.     aCompMgr = aCompMgr.QueryInterface(
  3970.       Components.interfaces.nsIComponentRegistrar);
  3971.     aCompMgr.registerFactoryLocation(CLASS_ID, CLASS_NAME, CONTRACT_ID,
  3972.       aFileSpec, aLocation, aType);
  3973.     aCompMgr.registerFactoryLocation(TEST_CLASS_ID, TEST_CLASS_NAME,
  3974.       TEST_CONTRACT_ID, aFileSpec, aLocation, aType);
  3975.   },
  3976.  
  3977.   unregisterSelf : function (aCompMgr, aLocation, aType) {
  3978.     yDebug.print("unregisterSelf: aLocation => " + aLocation, YB_LOG_MESSAGE);
  3979.     yDebug.print("unregisterSelf: aType => " + aType);
  3980.     aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  3981.     aCompMgr.unregisterFactoryLocation(CLASS_ID, aLocation);
  3982.     aCompMgr.unregisterFactoryLocation(TEST_CLASS_ID, aLocation);
  3983.   },
  3984.  
  3985.   getClassObject : function(aCompMgr, aCID, aIID) {
  3986.     yDebug.print("getClassObject: aCID => " + aCID);
  3987.     yDebug.print("getClassObject: aIID => " + aIID);
  3988.     if (!aIID.equals(Components.interfaces.nsIFactory)) {
  3989.       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  3990.     }
  3991.  
  3992.     if (aCID.equals(CLASS_ID)) {
  3993.       return YBookmarksStoreServiceFactory;
  3994.     } else if (aCID.equals(TEST_CLASS_ID)) {
  3995.       return YBookmarksStoreServiceTestFactory;
  3996.     }
  3997.     throw Components.results.NS_ERROR_NO_INTERFACE;
  3998.   },
  3999.  
  4000.   canUnload : function(aCompMgr) {
  4001.     return true;
  4002.   }
  4003. };
  4004.  
  4005. function NSGetModule(aCompMgr, aFileSpec) {
  4006.   yDebug.print("YBookmarksStoreServiceModule2 GetModule");
  4007.  
  4008.   return YBookmarksStoreServiceModule;
  4009. }
  4010.